Commit Graph

1258 Commits

Author SHA1 Message Date
shenlan
c1213a83b0 Update xstream subscription links and hide billing options (#687) 2025-11-21 21:59:05 +08:00
shenlan
bff6b75523 Refine subscription payment layout (#686) 2025-11-21 21:54:25 +08:00
shenlan
9cd61b1439 Move billing and subscriptions into user panel (#685) 2025-11-21 21:40:55 +08:00
shenlan
d63bef3a95 Annotate product payment methods (#684) 2025-11-21 20:56:01 +08:00
shenlan
6d2931c3d5 Make PayPal subscription plan ID optional (#683) 2025-11-21 20:44:53 +08:00
shenlan
05a02edae9 Add types for PayPal button callbacks (#682) 2025-11-21 19:54:35 +08:00
shenlan
080f34dfe3 Ensure PayPal SDK is treated as loaded (#681) 2025-11-21 19:30:05 +08:00
shenlan
d8d7a136eb Add crypto billing options and trial subscriptions (#680) 2025-11-21 19:20:51 +08:00
shenlan
c00898d712 Add PayPal billing and subscription support (#679) 2025-11-21 18:55:12 +08:00
Haitao Pan
dc95c645eb add manifest generation scripts
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 12:30:48 +08:00
Haitao Pan
0089f9d447 feat(content): separate content from code with markdown files
### Changes:

1. **Created content directories**:
   - `src/content/homepage/{zh,en}/hero.md` - Homepage content
   - `src/content/product/{xstream,xcloudflow,xscopehub}/{zh,en}/hero.md` - Product content

2. **Content generation script**:
   - `scripts/generate-content.ts` - Compiles markdown to JSON
   - Uses js-yaml for proper YAML parsing
   - Generates JSON files in `src/data/content/`

3. **Content loader**:
   - `src/lib/content-loader.ts` - Loads content from JSON files
   - No longer uses `fs` module (client-side compatible)
   - Supports homepage and product content

4. **Updated homepage component**:
   - `src/modules/homepage/page.tsx` - Now uses content loader
   - Removed hardcoded heroContent object
   - Content separated from logic and styling

### Result:
 Content separated from code
 Markdown format for easy editing
 Layout and logic unchanged
 Build succeeds with 120 static pages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 12:17:12 +08:00
Haitao Pan
c548b9fc74 feat(blog): add sticky navigation header
Add top navigation similar to dynamic product pages:
- SVC.plus/blog breadcrumb (clickable, returns to homepage)
- Search component on the right
- Sticky positioning with blur background

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 11:29:56 +08:00
Haitao Pan
4c5b2dcf67 fix(download): resolve static generation bailout error
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 11:07:50 +08:00
Haitao Pan
f4678a601a fix(docs): resolve static generation error with caching strategy
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 10:49:45 +08:00
Haitao Pan
31242cfa91 feat(Navbar): hide navigation on /blog routes
Hide navbar by default on /blog and its subpaths

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 10:39:09 +08:00
shenlan
5d1f442584 feat(dashboard): fetch download manifests dynamically (#675) 2025-11-11 23:19:43 +08:00
Haitao Pan
eae77beeea feat: support dynamic port and manifest URLs
1. start script: npm run start passes args to next start
   - Usage: npm run start -- -p 3000

2. /docs: dynamic fetch from docs-manifest.json
   - URL: https://dl.svc.plus/dl-index/docs-manifest.json

3. /download: dynamic fetch from offline-package-manifest.json
   - URL: https://dl.svc.plus/dl-index/offline-package-manifest.json

🤖 Generated with Claude Code
2025-11-11 22:32:21 +08:00
Haitao Pan
086ab779a4 fix: add local fallback data for static development mode
### Changes:

1. **Added fallback data files**:
   - `public/_build/artifacts-manifest.json` - Local fallback for downloads page
   - `public/_build/offline-package.json` - Local fallback for offline-package

2. **Updated data fetching logic**:
   - `dl-index-data-artifacts.ts` - Now uses local fallback when network fetch fails
   - `dl-index-data-offline-package.ts` - Now uses local fallback when network fetch fails

### Problem:
In static development mode (`yarn start -p 3000`), the dashboard's docs and downloads pages
could not fetch data from external URLs, resulting in empty content.

### Solution:
Added local fallback data files that are imported directly into the components.
When network requests fail (e.g., in offline mode or behind firewall), the app now
falls back to local static data, ensuring pages work in development mode.

### Data Flow:
1. Try fetching from `https://dl.svc.plus/dl-index/*.json`
2. If that fails, try fallback URL
3. If that also fails, use local `public/_build/*.json` data
4. Display content to user

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 21:52:13 +08:00
shenlan
1cf14f3fd5 Fix dashboard download imports and registry alias (#672) 2025-11-11 19:48:16 +08:00
Haitao Pan
d647d02992 feat: merge quick wrapper into main manifest scripts
### Changes:

1. **gen_offline-package_manifest.py**
   - Merged gen_offline-package_manifest_quick.py functionality
   - Added auto-derivation of --root from --output path
   - Auto-discovers subdirectories when --include not specified
   - Outputs to offline-package-manifest.json
   - Removed quick wrapper script (merged into main)

2. **gen_docs_manifest.py**
   - Added auto-discovery of subdirectories
   - Scans all category/version/*.html and *.pdf when --include not specified

### Usage:

For gen_offline-package_manifest.py:
  # Auto-derive root from output
  python3 scripts/gen_offline-package_manifest.py \
    --output /data/update-server/dl-index

  # Or specify root explicitly
  python3 scripts/gen_offline-package_manifest.py \
    --root /data/update-server/offline-package \
    --output /data/update-server/dl-index

For gen_docs_manifest.py:
  # Auto-discover all subdirectories
  python3 scripts/gen_docs_manifest.py \
    --root /data/update-server/docs \
    --output /data/update-server/dl-index

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 18:58:37 +08:00
Haitao Pan
196b11bcc5 feat: enhance scripts with filtering and offline-package support
### Changes:

1. **scripts/gen_docs_manifest.py**
   - Added --include parameter with default "docs"
   - Filters documentation files to only include specified directories
   - Can be provided multiple times for multiple directories
   - Updated docstring to document the new parameter

2. **scripts/gen_mirror_manifest.py**
   - Added generation of offline-package.json file
   - Filters listings to only include offline-package directory entries
   - Writes to the same output directory alongside manifest.json

### Usage:

For gen_docs_manifest.py:
  python3 scripts/gen_docs_manifest.py \
    --root /data/update-server/docs \
    --base-url-prefix https://dl.svc.plus/docs \
    --include docs

For gen_mirror_manifest.py:
  python3 scripts/gen_mirror_manifest.py \
    --root /data/update-server \
    --include offline-package \
    --output dl-index/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 18:33:04 +08:00
Haitao Pan
8d94183c97 feat: enhance scripts with filtering and offline-package support
### Changes:

1. **scripts/gen_docs_manifest.py**
   - Added --include parameter with default "docs"
   - Filters documentation files to only include specified directories
   - Can be provided multiple times for multiple directories
   - Updated docstring to document the new parameter

2. **scripts/gen_mirror_manifest.py**
   - Added generation of offline-package.json file
   - Filters listings to only include offline-package directory entries
   - Writes to the same output directory alongside manifest.json

### Usage:

For gen_docs_manifest.py:
  python3 scripts/gen_docs_manifest.py \
    --root /data/update-server/docs \
    --base-url-prefix https://dl.svc.plus/docs \
    --include docs

For gen_mirror_manifest.py:
  python3 scripts/gen_mirror_manifest.py \
    --root /data/update-server \
    --include offline-package \
    --output dl-index/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 17:53:36 +08:00
Haitao Pan
5522ea1c1b feat: integrate offline-package data source
### Changes:

1. **scripts/gen_mirror_manifest.py**
   - Added generation of offline-package.json file
   - Filters listings to only include offline-package directory
   - Writes to the same output directory (dl-index/)

2. **src/lib/download/dl-index-data-offline-package.ts**
   - New module to fetch offline-package data from CDN
   - Fetches from: https://dl.svc.plus/dl-index/offline-package.json
   - Provides helper functions:
     - fetchOfflinePackageListings()
     - getOfflinePackageListings()
     - getOfflinePackageSections()
     - getOfflinePackageFileCount()
   - Includes simple in-memory caching

3. **src/app/download/page.tsx**
   - Updated to use offline-package data
   - Merges offline-package sections with existing data
   - Added offline-package file count to totals
   - Made component async to support data fetching

### Result:
Download page now fetches and displays offline-package data from the CDN endpoint, providing real-time updates without rebuilding the application.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 17:50:41 +08:00
Haitao Pan
c69f4e81b4 feat: update base-url-prefix default in gen_mirror_manifest.py
### Changes:
- Changed --base-url-prefix default from "/" to "https://dl.svc.plus/offline-package"
- Updated docstring usage example to reflect the new default

### Reason:
Sets a more specific default URL prefix for offline package downloads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 17:44:07 +08:00
Haitao Pan
f000de44ce feat: enhance gen_mirror_manifest.py with configurable options
### Changes:
1. Set ROOT default to `/data/update-server/`
   - Changed --root from required to optional with default value

2. Add --include [dirlist] parameter
   - Default: offline-package
   - Can be provided multiple times
   - Filters which directories to include in manifest
   - Only processes directories matching the include list

3. Add --output parameter
   - Default: dl-index/
   - Specifies where to write manifest.json
   - Creates directory if it doesn't exist

### Usage:
  python3 scripts/gen_mirror_manifest.py \
    --root /data/update-server \
    --base-url-prefix / \
    --include offline-package \
    --output dl-index/ \
    [--exclude docs --exclude xray-core]

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 17:37:01 +08:00
Haitao Pan
c9ce622525 fix: unwrap async params in download route segment
### Issue:
Next.js App Router expects params to be a Promise that must be awaited before accessing properties.

### Error:
```
Route "/download/[...segments]" used `params.segments`.
`params` is a Promise and must be unwrapped with `await`
or `React.use()` before accessing its properties.
```

### Solution:
- Made component async: `export default async function DownloadListing`
- Updated params type: `Promise<{ segments: string[] }>`
- Unwrapped with await: `const { segments: rawSegments } = await params`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 17:10:00 +08:00
Haitao Pan
ee1894b146 fix: resolve TypeScript import errors for download types module
### Changes:
- Moved types from legacy 'types/download.ts' to '@lib/download/types'
- Updated all imports to use the new location:
  - src/components/download/DownloadListingContent.tsx
  - src/components/download/FileTable.tsx
  - src/lib/download-data.ts
  - src/lib/download-manifest.ts
  - src/app/download/[...segments]/page.tsx

### Why:
- Resolved TS6137 error: Cannot import type declaration files
- Aligns with codebase pattern: types co-located with modules (@lib/iac/types, @lib/mail/types)
- Uses consistent @ alias import pattern throughout codebase

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 17:08:46 +08:00
shenlan
6854ac32f9 Fix download page total file reducer typing (#670) 2025-11-11 14:47:53 +08:00
Haitao Pan
1225c0363f fix: resolve module import path errors
### Errors Fixed:

1. **Cloud IaC Index JSON Import** (src/app/cloud_iac/*/page.tsx)
   - Fixed incorrect relative import paths for cloud_iac_index.json
   - Updated to correct number of `../` to reach public directory
   - Both provider and service pages now import correctly

2. **Products Registry Import** (src/app/[slug]/Client.tsx)
   - Changed from non-existent `@src/products/registry` to `@modules/products/registry`
   - Uses existing path alias configured in tsconfig.json

3. **Download Type Import** (src/app/download/*/page.tsx)
   - Fixed import path from `../../../types/download` to `@types/download`
   - Added proper type annotation for filter callbacks to resolve TypeScript errors

4. **Docs Manifest Import** (src/app/docs/resources.server.ts)
   - Temporarily disabled invalid docs-manifest.json import (malformed JSON)
   - Falls back to docs_index.json
   - Build now passes without JSON parsing errors

### Result:
- Build errors reduced from 7 to 0
- All module imports resolved correctly
- TypeScript compilation passes
- Ready for production build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 14:38:22 +08:00
Haitao Pan
e8d82b956d perf: convert postcss.config to mjs to eliminate module parsing warning
### Issue:
PostCSS config file was using ES module syntax (export default) in a .js file without "type": "module" in package.json.

**Warning Message:**
"Module type of file is not specified and it doesn't parse as CommonJS. Reparsing as ES module because module syntax is detected. This incurs a performance overhead."

### Solution:
Renamed `postcss.config.js` to `postcss.config.mjs`:
- .mjs extension is automatically recognized as ES module
- No need to modify package.json
- Eliminates performance overhead warning
- Cleaner, explicit ES module indication

### Changes:
- **File renamed**: `postcss.config.js` → `postcss.config.mjs`
- **Content unchanged**: Still uses ES module export syntax
- **No package.json changes needed**

### Result:
-  PostCSS module parsing warning eliminated
-  Build still works correctly
-  Performance improved (no re-parsing)
-  Clear ES module indication

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 14:25:49 +08:00
Haitao Pan
fdf0323fad fix: add missing theme exports to resolve build error
### Issue:
ThemePreferenceCard was importing `darkTheme`, `lightTheme`, and `useTheme` from `../routes/theme`, but these exports didn't exist.

**Error:**
"Export darkTheme doesn't exist in target module"

### Solution:
Added missing exports to `src/modules/extensions/builtin/user-center/routes/theme.tsx`:

1. **Type Definitions:**
   - `ThemeName` - 'light' | 'dark'
   - `ThemePreference` - 'system' | 'light' | 'dark'
   - `ThemeDefinition` - Theme structure with name, colors, shadows
   - `ThemeContextValue` - Theme context value structure

2. **Theme Definitions:**
   - `lightTheme` - Light theme color tokens and shadows
   - `darkTheme` - Dark theme color tokens and shadows

3. **Hook:**
   - `useTheme()` - Stub implementation returning theme state and setter

### Result:
- Fixed "Export darkTheme doesn't exist" build error
- ThemePreferenceCard can now properly import all required exports
- Build errors reduced from 7 to 1 (pre-existing cloud_iac error remains)
- Theme component is functional with stub implementations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 14:19:07 +08:00
Haitao Pan
b011884ba5 feat: update Navbar to hide on dynamic product routes
### Task Completed:
1.  **Language Component Already Extracted**
   - LanguageToggle component is already in `src/components/LanguageToggle.tsx`
   - Uses global `useLanguage` hook from LanguageProvider
   - Already a shared component that can be reused anywhere

2.  **Updated Navbar to Hide on Dynamic Routes**
   - Modified `src/components/Navbar.tsx` to hide on `/xstream`, `/xcloudflow`, `/xscopehub`
   - Updated `isHiddenRoute` check to include these dynamic routes
   - Navbar automatically hides when visiting these product pages

### Changes:
**File: src/components/Navbar.tsx**
- Extended the hidden route list to include:
  - `/xstream` - XStream product page
  - `/xcloudflow` - XCloudFlow product page
  - `/xscopehub` - XScopeHub product page
- Existing hidden routes: `/login`, `/register`

### Result:
-  Navbar is hidden on all product landing pages
-  Language component remains globally accessible and shared
-  No code duplication - LanguageToggle is reusable
-  Clean separation of concerns

### Usage:
Any component can import and use the shared Language component:
```typescript
import LanguageToggle from '@/components/LanguageToggle'
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 14:10:18 +08:00
Haitao Pan
081f041162 fix: correct import path for LanguageProvider in SearchComponent
### Issue:
SearchComponent at `src/components/search/index.tsx` had incorrect import path for LanguageProvider.

**Error:**
"Module not found: Can't resolve '../i18n/LanguageProvider'"

### Root Cause:
- SearchComponent location: `src/components/search/index.tsx`
- LanguageProvider location: `src/i18n/LanguageProvider.tsx`
- Incorrect import: `../i18n/LanguageProvider` (only goes up 1 level)
- Correct import: `../../i18n/LanguageProvider` (needs to go up 2 levels)

### Fix:
Updated import path in `src/components/search/index.tsx`:
- Changed from: `import { useLanguage } from '../i18n/LanguageProvider'`
- Changed to: `import { useLanguage } from '../../i18n/LanguageProvider'`

### Result:
- Fixed module resolution error
- SearchComponent can now properly import and use LanguageProvider
- Blog page can successfully import and render SearchComponent
- Build passes successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 14:05:03 +08:00
shenlan
43cdc009f8 feat: extract shared search component (#669) 2025-11-11 14:02:23 +08:00
shenlan
cf7c017a72 Fix blog pagination search params handling (#668) 2025-11-11 13:50:17 +08:00
Haitao Pan
fff6816077 fix: add force-dynamic directive to blog page for searchParams support
### Issue:
The root layout has `export const dynamic = 'error'` which forces static rendering.
The blog page uses `searchParams` which requires dynamic rendering.

**Error Message:**
"Route /blog with `dynamic = "error"` couldn't be rendered statically because it used `searchParams`"

### Solution:
Added `export const dynamic = 'force-dynamic'` to `src/app/blog/page.tsx` to override
the parent's static rendering directive and enable dynamic rendering for searchParams.

### Changes:
- **BlogPage** - Added `export const dynamic = 'force-dynamic'` at the top of the file
- This allows the page to use `searchParams` for pagination
- Page will be dynamically rendered on each request with the correct page data

### Result:
- Fixed runtime error when accessing pagination params
- Blog list pages load successfully with `/blog?page=X`
- Pagination works correctly with dynamic searchParams
- Build passes successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:46:03 +08:00
Haitao Pan
987703c5e7 fix: handle async searchParams in blog pagination
### Issue:
Next.js 15 requires searchParams to be awaited as it's now a Promise.

**Error Message:**
"Route "/blog" used `searchParams.page`. `searchParams` is a Promise and must be unwrapped with `await`"

### Solution:
Updated `src/app/blog/page.tsx` to properly await searchParams before accessing its properties.

**Changes:**
- **BlogPage component** - Added `await searchParams` before accessing page property
- Used destructuring: `const { page } = await searchParams`
- Updated parseInt to use destructured `page` variable

### Result:
- Fixed runtime error when accessing pagination params
- Pagination now works correctly with async searchParams
- Blog list pages load successfully with `/blog?page=X`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:43:26 +08:00
Haitao Pan
a5d88a22b6 feat: add pagination and fix blog post links
### Changes to Blog Listing Page (src/app/blog/page.tsx):

1. **Implemented Pagination**
   - Added support for `page` query parameter (e.g., `/blog?page=2`)
   - Displays 10 posts per page
   - Shows pagination controls with Previous/Next buttons
   - Page numbers displayed as clickable buttons
   - Active page highlighted with brand color
   - Handles edge cases (invalid page numbers, out of bounds)

2. **Fixed "Read more" Links**
   - Changed from `/blog` to `/blog/${post.slug}`
   - Each blog card's "Read more" now links to individual article page
   - Clicking "Read more" on Cloud-Neutral-Hybrid-Network-Architecture → `/blog/Cloud-Neutral-Hybrid-Network-Architecture`

3. **Features**
   - **Server-side pagination**: Posts are sliced on the server
   - **URL-based navigation**: Pagination state in URL
   - **Responsive design**: Clean pagination controls
   - **Error handling**: 404 for invalid pages
   - **Navigation**: Previous/Next buttons with disabled states
   - **Accessibility**: aria-disabled attribute on navigation

### Example URLs:
- `/blog` - First page (default)
- `/blog?page=1` - First page
- `/blog?page=2` - Second page
- Each post card → `/blog/[post-slug]`

### Result:
-  Blog list supports pagination
-  "Read more" links work correctly
-  Better UX for browsing multiple blog posts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:42:11 +08:00
Haitao Pan
38b6f7ae01 fix: handle async params in dynamic blog route
### Issue:
Next.js 15 requires params to be awaited as it's now a Promise.

**Error Message:**
"Route "/blog/[slug]" used `params.slug`. `params` is a Promise and must be unwrapped with `await`"

### Solution:
Updated `src/app/blog/[slug]/page.tsx` to properly await params before accessing its properties.

**Changes:**
1. **generateMetadata function** - Added `await params` before accessing slug
2. **BlogPostPage component** - Added `await params` before accessing slug
3. Used destructuring: `const { slug } = await params`

### Result:
- Fixed runtime error when accessing blog posts
- Dynamic routes now work correctly with async params
- Blog post pages load successfully

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:37:45 +08:00
Haitao Pan
47306b2d41 feat: implement dynamic blog post routing with individual article pages
### Changes:

1. **Updated CommunityFeed Links** (`src/components/home/CommunityFeed.tsx`)
   - Changed "Read more" buttons to link to `/blog/${post.slug}`
   - Made post titles clickable with same link
   - Each article now navigates to its own full page

2. **Created Dynamic Blog Post Route** (`src/app/blog/[slug]/page.tsx`)
   - New dynamic route: `/blog/[slug]` for individual articles
   - Displays full article content with markdown rendering
   - Shows metadata: title, author, date, tags
   - Includes "Back to Blog" navigation
   - 404 handling for non-existent posts
   - Dynamic metadata generation for SEO
   - Responsive typography styling

### Features:
- **Individual Article Pages**: Each blog post has its own URL
- **Full Content Display**: Renders complete markdown content with HTML
- **Rich Metadata**: Shows author, date, and tags
- **Navigation**: Easy back-and-forth between list and article views
- **SEO Ready**: Dynamic metadata per post
- **Error Handling**: 404 page for invalid post slugs

### Usage:
- Click "Read more" or post title → `/blog/[slug]`
- View full article with all content
- Navigate back to blog list

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:35:46 +08:00
Haitao Pan
1e241ed8a8 fix: make CommunityFeed span full width of content section
### Change:
Modified content section layout to allow CommunityFeed to occupy the **full width** of the lower section (not constrained to left column only).

**File: commonHome.tsx**
- Removed grid constraint when Sidebar is present
- Content slots now use simple `<div>` wrapper instead of grid with spacer
- CommunityFeed can now utilize the entire content width
- Maintains visual consistency while maximizing content space

**Result:**
- CommunityFeed spans the full width of the content section
- Matches the total width of hero section (ProductMatrix + Sidebar)
- Better visual balance and more space for content

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:26:11 +08:00
Haitao Pan
3cbc9fbb7e 973e0ea - Adjust layout for consistent sidebar height and section width across homepage 2025-11-11 13:19:57 +08:00
Haitao Pan
973e0ea3f5 refactor: adjust layout dimensions for consistent width and height
### Changes:
1. **Hero Section Layout** (commonHome.tsx)
   - Changed `lg:items-start` → `lg:items-stretch` for equal height alignment
   - Added `min-h-full` to hero content column for full height
   - Ensures Sidebar height matches ProductMatrix height

2. **Content Section Layout** (commonHome.tsx)
   - When Sidebar present, uses matching grid structure from hero section
   - Grid: `grid-cols-[minmax(0,1fr)_360px]` (same as hero)
   - Content slots span full width with hidden spacer for visual balance
   - CommunityFeed now matches total width of (ProductMatrix + Sidebar)

3. **Sidebar Component** (Sidebar.tsx)
   - Added `h-full` and `flex flex-col` for full height support
   - Maintains sticky positioning while filling available space

4. **HomepageLanding** (page.tsx)
   - Applied same height adjustment: `lg:items-stretch` + `min-h-full`
   - Consistent with template version layout

### Result:
- Sidebar height = Hero section height (full height alignment)
- CommunityFeed width = ProductMatrix width + Sidebar width
- Visual consistency across hero and content sections
- Both homepage versions (template and direct) have identical layout

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:13:00 +08:00
Haitao Pan
10f0d14c0b fix: restore Sidebar to template system for hero section
### Issue:
Sidebar (社区热议, 推荐资源, 热门标签) was missing from CMS template version of homepage because it was only in HomepageLanding component.

### Solution:
1. **Restored Sidebar to Template Slots** (`src/modules/templates/types.ts`)
   - Added Sidebar back to HomePageTemplateSlots interface
   - Updated HomePageSlotKey type to include Sidebar

2. **Updated Template Registration** (`src/modules/templates/default/index.tsx`)
   - Added Sidebar import and registration to default template
   - Template now passes Sidebar slot

3. **Updated App Route** (`src/app/page.tsx`)
   - Added Sidebar to slot props in HomePageTemplate

4. **Modified Common Home Template** (`src/modules/templates/layouts/commonHome.tsx`)
   - Enhanced template to detect and render Sidebar in hero section
   - When Sidebar is available, renders two-column grid layout:
     - Left: ProductMatrix (hero content)
     - Right: Sidebar (360px width, sticky)
   - Grid: `lg:grid-cols-[minmax(0,1fr)_360px]`
   - Maintains backward compatibility if Sidebar not provided

### Result:
- Sidebar now appears in BOTH homepage versions (template and direct)
- Consistent layout: Sidebar positioned to the right of hero content
- Visual consistency with HomepageLanding component layout
- Bilingual support (Chinese/English) maintained

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:09:08 +08:00
Haitao Pan
0a55d3c4a1 refactor: move Sidebar to hero section with two-column layout
### Layout Changes:
1. **Homepage Layout Restructure** (`src/modules/homepage/page.tsx`)
   - Changed hero section to two-column grid: `lg:grid-cols-[minmax(0,1fr)_360px]`
   - Moved Sidebar component to right side of hero section (360px width)
   - Hero content now spans left column with full-width appearance
   - Sidebar is sticky on large screens: `lg:sticky lg:top-0`
   - Maintains consistent width between upper and lower sections

2. **Template System Updates**
   - Removed Sidebar from `HomePageTemplateSlots` type (`src/modules/templates/types.ts`)
   - Updated `defaultHomeLayoutConfig` to remove Sidebar from content slots
   - Updated default template to remove Sidebar registration
   - Updated `app/page.tsx` to pass only ProductMatrix and CommunityFeed slots

3. **Sidebar Component Conversion** (`src/components/home/Sidebar.tsx`)
   - Converted from Server Component to Client Component
   - Added 'use client' directive to enable useLanguage hook
   - Replaced dynamic CMS content with static bilingual content
   - Hardcoded sections: 社区热议, 推荐资源, 热门标签
   - Maintains same visual structure and styling

4. **Homepage Module Cleanup** (`src/modules/homepage/page.tsx`)
   - Removed unused `getHomepagePosts` import (was causing fs error in client)
   - Kept useLanguage hook for internationalization support
   - Sidebar now works as client component alongside homepage

### Technical Details:
- Grid layout: Two columns on large screens (hero + sidebar)
- Sidebar width: Fixed 360px, sticky positioning
- Hero content: Flexible width with max-w-6xl constraint
- Visual consistency: Upper and lower sections maintain same max-width
- Component architecture: All client components, no server-client mixing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 13:06:49 +08:00
Haitao Pan
7a6bd66ad0 refactor: separate blog content from code
### Changes:
1. **Create Blog Content Directory**
   - Created src/content/blog/ directory for content management
   - Added README.md with usage instructions
   - Added 3 sample blog posts

2. **Update Content Retrieval Function**
   - Modified getHomepagePosts() in homepage.ts
   - Now reads from src/content/blog/ instead of CMS config
   - Content and style are now separated

3. **Content Management**
   - Style: Keep existing components and styles
   - Content: Manage via markdown files in src/content/blog/
   - Metadata: YAML front matter (title, author, date, tags, excerpt)
   - Sorting: Sort by date (newest first)

### Benefits:
- Content separated from code
- Easy for non-technical users to add content
- Version control friendly
- Unified metadata management

To add new blog posts:
1. Create .md file in src/content/blog/
2. Use standard front matter format
3. Use kebab-case for filenames

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-11 12:58:49 +08:00
Haitao Pan
851c82ea7b refactor: remove ArticleFeed, keep only CommunityFeed
- Remove ArticleFeed and ArticleFeedClient components
- Update template types to remove ArticleFeed
- Update default template configuration
- Update app/page.tsx to remove ArticleFeed
- Keep only CommunityFeed for blog updates (latest 3 posts)
- All blog content now uses CommunityFeed

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-11 12:55:23 +08:00
Haitao Pan
ae1c77aada feat: add CommunityFeed to template configuration
- Update HomePageTemplateSlots type to include CommunityFeed
- Add CommunityFeed slot to default layout config
- CMS content now displays CommunityFeed between ArticleFeed and Sidebar

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-11 12:53:05 +08:00
Haitao Pan
c36a9e0d17 fix: restructure CommunityFeed to separate Server and Client logic
- CommunityFeed now Client Component (accepts posts via props)
- Created CommunityFeedServer wrapper to fetch posts on server
- Fixes Module not found: Can't resolve 'fs' error
- HomepageLanding no longer imports CommunityFeed
- app/page.tsx passes CommunityFeedServer to template

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-11 12:51:12 +08:00
Haitao Pan
6d16ed0289 fix: dynamic import CommunityFeed in HomepageLanding
- Use Next.js dynamic() to import CommunityFeed as Server Component
- Fixes Module not found: Can't resolve 'fs' error
- Client Component can now safely import Server Component

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-11 12:48:21 +08:00