copy-routes now emits both foo.html and foo/index.html so the unchanged nginx serves docs/sdk (.html) and the catch-all (/index.html). Fix an invalid empty selector in api-reference.css that broke lightningcss minify. Exclude migration URLs from the sitemap. Point the release/canary deploy at dist/ instead of the removed docs/.vitepress/dist (was uploading an empty dir → no md/html on OSS).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Header is now position:fixed (like legacy) so it stays pinned during overscroll; #main-content reserves its 60px. Rebuild the mobile top nav (icon hamburger, accordion Features, overlay panel) and the docs local-nav (reveal-on-scroll bar + On this page TOC). Fixed-header/mobile fixes: 60px logo→menu gap, mobile tail alignment, footer 2-col + centered bottom bar, breadcrumb/doc-footer alignment, symmetric mobile padding. MCP page excluded from the docs sidebar (sidebar:false). Migrate the login redirect logic from the legacy AppNav: createLoginRedirectPath (/login?redirect_to=<sso?redirect_to=current>&logout=1&with-us=1), login state via window.longportInternal, and the logged-in Dashboard + avatar dropdown; UserAvatar now renders the full login block.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port the legacy VitePress homepage (10 sections: hero, channels, features, CLI spotlight, AI Skill, MCP, API capabilities, SDK, get-started, CTA) and the Skill page (hero, demo, catalog, capability, cases, get-started) as hydrated React islands, matching legacy markup/CSS and interactions. Add a shared CopyButton with copied feedback across the homepage code blocks. Remove 14 orphaned homepage sections and the old SkillCatalog; wire the home/skill islands via the [...slug] routes and the mdx-component map.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Docs pages render a slim link-row footer (DocFooter) instead of the big
marketing footer; BaseLayout gains a hideFooter prop
- Callout blocks restyled to the legacy TipContainer look (1px border, radius,
per-type icon, accent-coloured title + body)
- Doc meta row (Markdown / Edit / Last Updated) via DocMeta; real per-page
dates from origin/main git history (src/lib/last-updated.ts)
- Body copy: antialiased smoothing + faint rgba(0,0,0,.06) underline colour to
match legacy .vp-doc
- Heading anchors moved to the left gutter; scroll-margin-top offsets the 61px
sticky header on TOC/anchor jumps
- TOC pinned at its at-rest offset so it no longer jumps up on scroll
- Breadcrumb: bold current-page crumb
- Sidebar: childless category folders (e.g. cli/ipo) render as clickable bold
headers instead of dead muted leaves
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to the sidebar refactor. The legacy sidebar shows a top-level
"Overview" (/docs) as the first, active item — it's docs/{locale}/docs/
index.mdx (title Overview / 平台介绍 / 平台介紹, sidebar_position -999,
book_open icon). buildItems() skipped EVERY index.mdx, so that page never
appeared in the tree and /docs had no active sidebar item.
buildItems() gains an `includeIndex` param (default false). buildSidebar's
root call passes true so the root index.mdx is emitted as a top-level leaf
(link /docs, /zh-CN/docs, /zh-HK/docs via urlFromAbsPath; -999 sorts it
first). Nested index.mdx files still skip — they're absorbed as directory
links via _category_.json, unchanged.
Applies to both scopes: docs/{locale}/docs/index.mdx and the cli sidebar's
docs/{locale}/docs/cli/index.mdx (both exist in all three locales).
Verified: /docs now shows "Overview" with book-open icon + teal active
pill at the top of the first group, matching the legacy reference.
navigation.test.ts 3/3 pass (non-empty, zh-CN count == en count, position
ascending all still hold — the new node's -999 sorts first).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gate-4 flagged three A-class residuals blocking cutover:
- **A5/A21** — Homepage tab title `(untitled)` on / and /zh-CN/.
index.mdx frontmatter uses `titleTemplate` (not `title`); the 3
[...slug].astro dispatchers were hard-fallback to '(untitled)'.
Now cascade: title → titleTemplate → 'Longbridge Developers'.
content.config.ts schema gains a `titleTemplate` z.string().optional().
- **A19** — /sdk hero SVG rendered as literal text `/assets/sdk.svg`.
Stage-1 T11 SDK.tsx placeholder shipped a text stub instead of
<img>. Ported the layout from legacy SDK.vue (max-w-4xl 2-col hero
with h-244 svg on the right). Asset copied to
public/assets/sdk.svg (matches legacy `docs/public/assets/sdk.svg`
path convention).
- **B8** (new) — /zh-CN/ hero rendered English strings ("Powering",
"Get Started") despite Chinese nav. Root: mdx `<NewHomePage />`
tag has no way to thread route locale through <Content components>.
Solved via `buildMdxComponents(locale)` factory that binds locale
at SSR time to any component with a `locale?` prop
(SkillCatalog / NewHomePage / McpTools / Pricing / QuotePermission
/ TryIt). Three [...slug].astro files now call
`buildMdxComponents('en'|'zh-CN'|'zh-HK')` per locale.
NewHomePage also grows a client-side detectLocale() fallback via
document.documentElement.lang for edge cases where propsLocale is
absent (defensive; the factory ensures it's always passed).
Verified:
- astro check: 0 errors (138 files)
- / SSR: "Powering" / "Get Started" (en)
- /zh-CN/ SSR: "接入" / "快速开始" (Simplified)
- /zh-HK/ SSR: "接入" / "快速開始" (Traditional)
- /sdk SSR: <img class="h-[244px]" src="/assets/sdk.svg" alt="">
- /assets/sdk.svg → 200
Gate-4 A-class findings all closed; cutover unblocked pending §S12
(nginx) + §S13 (canary).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sprint 2 §S8. Ports legacy `docs/.vitepress/theme/components/NewHomePage/`
(16 vue files, 9122 lines) to React inside a new `packages/homepage`
workspace published as `@longbridge/openapi-homepage`. Chunked-read
protocol across four subagent batches (§S8, §S8-b, §S8-c, §S8-d) since
individual vue files were too large for a single subagent context (up
to 930 lines each; index.vue at 2912 lines was skipped in favour of a
purpose-built React aggregator).
Sections ported (15):
- HeroSection (352 lines vue)
- CoreFeaturesSection (290)
- CapSection (236)
- ArchSection (231; wires SdkMarquee with SDK data lifted from vue)
- ArchCanvas (498; pure CSS flexbox arch diagram — no @xyflow/react
needed after all)
- MarketCoverage (177)
- PlatformStats (458)
- ProductOpenAPI (668)
- ProductCLI (730)
- ProductMCP (792)
- ProductSkill (930)
- SdkMarquee (401)
- GetStarted (278)
- StyleToggle (36; kept as controlled widget, not page section)
- HomepageFooter (133; distinct from site-wide shell/Footer)
Files created:
- packages/homepage/package.json — bun workspace; @longbridge/openapi-utils dep
- packages/homepage/tsconfig.json
- packages/homepage/src/index.ts — barrel
- packages/homepage/src/NewHomePage.tsx — aggregator, imports all 15
sections in the same order as legacy index.vue
- packages/homepage/src/ArchCanvas.tsx
- packages/homepage/src/homepage.css — page-level styles
- packages/homepage/src/sections/*.tsx — 15 section files
Files modified:
- src/mdx-components.tsx — swap import from placeholder to
`@longbridge/openapi-homepage`
- root package.json — workspace dep
Files deleted:
- src/components/mdx/placeholders/NewHomePage.tsx
Verified:
- astro check: 0 errors (113 files)
- 7 canonical URLs return 200
- data-lbus-component tags visible in /: hero-section, core-features,
cap-section, arch-section, market-coverage, platform-stats,
product-openapi, product-cli, product-mcp, product-skill, sdk-marquee,
arch-canvas, get-started, homepage-footer, new-home-page
Deferrals:
- SdkMarquee is wired through ArchSection with the SDK_LIST hoisted
out; if legacy shows SdkMarquee elsewhere on the page, that mount
point will surface in the gate visual diff and can be adjusted
- Animations (parallax, typewriter, particles, gradient shifts) match
legacy behavioral shape; pixel-perfect timing may need a second pass
in §S11 gate
Closes Sprint 2 §S8 (gate finding A1 / A5 / A6 / A7 / A21).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sprint 2 §S5: move src/lib/{slug,region,navigation,i18n}.ts + tests
into a new bun workspace `packages/utils` published as
`@longbridge/openapi-utils`. Consumers import from the package name
instead of relative paths or `@lib/*` aliases.
Files moved (git mv preserves history):
- src/lib/slug{,.test}.ts → packages/utils/src/
- src/lib/region{,.test}.ts → packages/utils/src/
- src/lib/navigation{,.test}.ts → packages/utils/src/
- src/lib/i18n.ts → packages/utils/src/
New package skeleton:
- packages/utils/package.json (private workspace, "@longbridge/openapi-utils")
- packages/utils/tsconfig.json (extends root, includes .astro types
so `astro:content` resolves in isolated TS server)
- packages/utils/src/index.ts (barrel re-exports)
Consumer updates (22 files): shell components, layouts, [...slug]
routes on all 3 locales, llms.txt endpoints, md endpoint — all import
from `@longbridge/openapi-utils` now.
tsconfig.json — removed `@lib/*` alias (dead after the move); other
aliases (`@/*`, `@components/*`, `@styles/*`, `@data/*`) intact.
Two slug.test.ts expectations updated to reflect the T3 semantics
locked in during Sprint 1.5:
- absolute slug in `en/docs/**` → `/docs/{slug}` (was `/{slug}`)
- absolute slug in `zh-CN/docs/**` → `/zh-CN/docs/{slug}` (was `/zh-CN/{slug}`)
- new coverage: absolute slug for `en/{marketing}.mdx` still site-absolute
The test file was carrying pre-T3 expectations; §S5 is when
they had to move regardless, so the correction lands here.
Verified:
- astro check: 0 errors (81 files)
- vitest: 23/23 pass
- 8 canonical URLs: all 200
- zero residual `../lib/*` / `@lib/*` import paths anywhere in
src/ or packages/
Known deferred:
- `packages/utils/src/i18n.ts` still reads locale data via
`../../../src/data/locale.*` (cross-boundary). Acceptable for §S5
scope; §S10 may relocate locale data into the utils package.
Closes Sprint 2 §S5.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sprint 1.5 mini §S11 gate flagged (A8, A12, A20):
- vitepress `:::warning`/`:::tip`/`:::success` containers render as raw
text ("`:::warning Package Renamed`" appears in body) — 335 usages
- `<Tabs><TabItem>` ships to browser but tab bar never appears; all
TabItems stack under each other (e.g. macOS/Windows/Linux install
commands, /sdk language SDK panels)
A8 — Callout directive rendering
--------------------------------
remark-directive v4 (micromark-extension-directive) rejects the bare
title form `:::warning Package Renamed` and only accepts bracket labels.
Two-part fix:
- Preflight Rule 10 rewrites `:::name Title` → `:::name[Title]` in mdx
source before the remark pipeline; scoped to known names
(success/warning/tip/info/danger/note/caution) and never rewrites
lines that already use `[…]` or `{…}` syntax.
- New remark plugin `src/integrations/remark-callout.ts` transforms
parsed `containerDirective` AST nodes into
`<div class="callout callout-{name}" role="note"
data-lbus-component="callout-{name}">` with an optional
`<p class="callout-title">` from the directive label (default:
capitalised name).
- `src/styles/callout.css` maps each variant to `--lb-status-*` /
`--lb-brand` tokens: warning=orange, danger=red, tip=teal,
success=green, info/note/caution=blue.
A12/A20 — Tabs SSR + client hydration
--------------------------------------
Root cause: Astro renders each React component in mdx in its own React
root — `<Tabs>` children arrive as pre-serialised HTML, so
`React.Children.forEach` yields nothing, and `<TabItem>` never sees
`TabsContext.Provider`. The `useState`+`useEffect`+`registerTab` pattern
was silently no-op'ing.
Rebuilt as progressive-enhancement:
- `packages/ui/src/TabItem.tsx` — `!ctx` branch emits
`data-tab-value`/`-label`/`-default` attrs for a client script to
discover. TabItem's public props unchanged.
- `packages/ui/src/Tabs.tsx` — removes broken `useMemo` introspection;
SSR renders `<div data-lbus-component="tabs" data-tabs-group-id=…
data-tabs-variant=…>` with an empty `<div data-tabs-bar>` placeholder
and children below. `registerTab`/localStorage machinery retained
for future client:load hydration paths.
- `src/scripts/tabs-hydrate.ts` — vanilla-JS module bundled by Vite.
On `DOMContentLoaded` walks `[data-lbus-component="tabs"]` wrappers,
reads TabItem attrs, builds `<button>`s into the empty bar, hides
non-active TabItems via inline style, wires click handlers, honours
the existing localStorage `vitepress-tabs-{groupId}` key + syncs
cross-instance via `__LBTabsState` / `__LBTabsListeners`.
- `src/layouts/BaseLayout.astro` — appends
`<script>import '../scripts/tabs-hydrate'</script>` before `</body>`.
The vanilla-JS path deliberately mirrors the pre-existing React
`__LBTabsState` API so future migrations back to a React-hydrated Tabs
(e.g. under `client:load`) can coexist without behavioural diff.
Verified:
- astro-check green (after adding `export {}` to tabs-hydrate.ts so
`declare global` is legal in a module context)
- 8 canonical URLs return 200
- chrome-devtools MCP 1440×900 hard-reload:
- /docs/cli/install shows 4 tabs (macOS Homebrew / Linux macOS
Script / Windows Scoop / Windows PowerShell), only active tab's
code visible
- /sdk shows orange "Package Renamed" callout with border-left +
tinted background; 6 language tabs (Python / JavaScript / Rust /
Java / Go / C++) render, only active tab visible
Closes Sprint 1.5 T5. Sprint 1.5 remediation complete — next: re-run
mini §S11 gate before beginning Sprint 2.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stage-1 shipped skeleton with semantic BEM classNames (top-nav,
docs-layout, sidebar, etc.) but no CSS. Hotfix 7d8f5ff4 added a 567-line
stopgap src/styles/shell.css to unblock the demo. This commit migrates
each component's className strings to inline Tailwind v4 utility classes
per spec §T3, then removes the stopgap file:
- 15 shell/ components (TopNav, Footer, Breadcrumb, LocalNav, Sidebar,
SidebarItem, Backdrop, TOC, PrevNext, ThemeToggle, LanguageSwitcher,
SearchButton, SearchDialog, SearchResults, UserAvatar) — BEM → Tailwind
utility, dark mode via CSS token auto-swap.
- 3 packages/ui/ components — Tabs (`.tab-line-btn` removed; inline
style already complete), TabItem (`.tab-item` → `py-4`), CliCommand
(legacy `.language-bash .vp-adaptive-theme .cli-command-block` classes
removed; `.relative` inline).
- shell.css deleted; @import removed from global.css.
Layout grid (`.docs-layout`) and mdx-emit typography (`.docs-content`
h1/h2/pre/code/blockquote) extracted to src/styles/docs.css and
imported from global.css — these style DOM emitted by mdx that cannot
be Tailwind-utility-inlined.
Token references normalised: previously-undefined `--lbus-c-border`,
`--lbus-c-text-muted`, `--lbus-c-brand`, `--lbus-c-bg-soft` (which
fell back to `currentColor` / transparent in the stopgap) replaced with
their real values in tokens.css: `--lb-stroke`, `--lb-fg-2`,
`--lb-brand`, `--lb-bg-2`.
Verified:
- astro-check green (0 errors, 5 pre-existing hints)
- 884/884 dev URLs return 200
- Chrome DevTools MCP screenshot 1440x900: top nav / sidebar / content /
TOC / footer all render (verified against vitepress baseline; residual
pixel-parity gaps — Features dropdown, GitHub / Get Started CTAs,
sidebar emoji icons, breadcrumb full path, mdx table styling — are
stage-1 skeleton residuals not introduced by this codemod; queued for
Sprint 1.5 mini §S11 gate).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Without an explicit "types" field, VS Code tsserver falls back to
seeking .d.ts files and emits TS2307 for the workspace import.
astro check is unaffected (Vite bundler resolver reads exports map)
but the editor diagnostic appears in every developer session.
Ruling in
.superpowers/sdd/2026-08-17-astro-migration-stage-1/progress.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>