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>
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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>
### 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>