Boss noticed the article font differed. Legacy self-hosts Inter (woff2) and
renders `.vp-doc` content in Inter 15px / ~1.75 line-height — a different
face from the SF Pro Display used by the site chrome (nav/marketing). Our
Astro build used SF Pro Display for everything, so the doc body was the
wrong typeface + size (16px).
- Added `@fontsource-variable/inter` (self-hosted variable font, same
approach as legacy — no external Google Fonts request).
- Imported it in global.css.
- `.docs-content` now uses `"Inter Variable", Inter, ui-sans-serif,
system-ui, …` at 15px / 1.75 line-height (headings inherit Inter; code and
tables keep their mono / 14px). Site chrome keeps SF Pro Display.
Verified: Inter Variable loads (document.fonts.check true); .docs-content p
= Inter 15px, h1 = Inter 32px; nav still SF Pro Display.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Boss test: dev environment showed "Search is available after first
production build" — pagefind only generates its index during
`bun run build`, which was flagged as a broken user experience.
Swap the whole search substrate to build-time JSON index +
client-side minisearch:
- **`src/pages/search-index.[locale].json.ts`** (new) — Astro dynamic
route emitting `/search-index.{en,zh-CN,zh-HK}.json`. Each doc is
sliced into sections (one per heading), each carrying the ancestor
heading breadcrumb (h1 → h2 → h3) so the client can render the
legacy `# Board - Security Board` / `# Scene Demonstration >
Submit Order` layout without a second round-trip. Body text is
stripped of MDX / code fences / markdown syntax and capped at 2000
chars per section. Runs during both `astro dev` and `astro build`
— dev serves it live, so the search works from the first click.
Region-filtered via `includedInRegion` so CN builds get the CN
sitemap.
- **`SearchDialog.tsx`** — pagefind removed; minisearch@7 added.
Per-locale index cached in a module-level `Map` so subsequent opens
are instant; first open pays one fetch + `addAll`. Custom
`tokenize` splits CJK into per-character tokens while keeping
western words whole — needed because minisearch's default whitespace
tokenizer produces zero tokens for `实时行情报价`. Fields boosted
`title:3, headings:2, body:1`, `prefix + fuzzy 0.15`. Debounce
effect re-fires on `status` change so a query typed while the
index is still loading gets served the moment it's ready. Dialog
chrome updated to match legacy image #25 — search icon, clear
button, keyboard hint bar (↑↓ Switch · ↵ Select · esc Close), and
the same `--app-mono` kbd styling as the ⌘K badge.
- **`SearchResults.tsx`** — new UI:
# <h1 title> > <h2 title> > <h3 title>
with matched query terms wrapped in a teal `<mark>` (against
`--lb-fg-invert`), and the deepest heading rendered bold. Selected
row gets a teal border + inset ring — same active-state affordance
as the legacy screenshot.
- **De-dup guard** — the section builder occasionally emits two
sections with identical `id` when a doc reuses a heading text at
the same depth (e.g. two `## Examples` blocks); MiniSearch throws
on duplicate adds. Filtered client-side before `addAll`.
- **extractField gotcha** — MiniSearch v7's `extractField` is used
for BOTH tokenization AND `storeFields`. Naively stringifying every
field turns the `headings: string[]` array into "a,b,c" in the
stored result, and the UI's `hit.headings.map(...)` crashed with
"map is not a function" — the dialog disappeared to a blank
fallback. Fixed by returning raw values for non-virtual fields and
only stringifying the synthetic `headingsJoined` for tokenization.
Verified in Chrome DevTools: dev-served /search-index.en.json returns
2698 sections (1.1 MB), first-open build takes ~800 ms, typing
"security" returns 12 hits including "# Security News",
"# longbridge security-list › Examples", "# longbridge security-list
› Examples › List securities by market" — exactly the legacy shape.
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 preflight stripped `## Foo {#bar}` syntax to unblock MDX parsing,
leaving all `#anchor` deep-links broken across the site (spec §-1 URL
parity requirement).
Two-path anchor pipeline:
1. Plain headings (`## Foo`): rehype-slug auto-generates slugified id,
rehype-autolink-headings appends `.header-anchor` link.
2. Explicit-id headings (`## Foo {#bar}`): preflight Rule 2 no longer
strips — it now converts to JSX `<h2 id="bar">Foo<a …/></h2>` BEFORE
MDX/acorn parses the `{#bar}` expression. Preserves explicit ids
which matter for zh-CN/zh-HK where heading text ≠ id
(e.g. `## 频率限制 {#rate-limit}`).
Both paths emit equivalent HTML: `<h{n} id="…">…<a class="header-anchor"
aria-hidden="true" tabindex="-1"></a></h{n}>`.
CSS for `.header-anchor` appended to tokens.css (uses `--lb-fg-2` muted
token; `--lbus-c-text-muted` was proposed in plan but does not exist).
Verified:
- astro-check green
- dev-side: en `id="rate-limit"`, zh-CN `id="接口类型"` + `id="rate-limit"`,
zh-HK equivalent
- 884/884 URLs return 200
Deviation from S3 plan: Rule 2 kept and rewritten rather than removed.
Root cause of plan text drift: acorn parses JSX expressions before
remark plugins run, so remark-heading-id cannot intercept `{#bar}` in
mdx source — the smart-conversion approach is the correct fix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Boss verified the stage-1 spec/plan closure only checked astro-check + unit
contracts; no reviewer ever started dev. First `bun run dev` revealed that
the entire shell was unstyled and 884 content routes had 0/500/404 rates
scattered across mdx edge cases. This is the full recovery pass.
- content.config.ts: Astro 5+ glob loader was using frontmatter `slug:` as
entry.id, deduping ~410 tri-locale entries and breaking resolveUrl. Force
generateId to derive from file path. Introduce `docs_layout` schema
field for the renamed frontmatter.
- 3× [...slug].astro: dispatch on `docs_layout` (renamed by preflight).
- SearchDialog.tsx: hoist `/pagefind/pagefind.js` URL into a runtime var
and HEAD-probe before dynamic import so Vite's static analyzer doesn't
fail the whole page when the pagefind bundle isn't built yet.
- global.css: add explicit @source directives so Tailwind v4 scans src/,
packages/ui/, and docs/ for utility classes.
- shell.css (new, ~330 lines): stopgap CSS for the semantic BEM classNames
the shell/mdx composite subagents shipped without CSS. Stage-2 will
codemod each into Tailwind utilities colocated on components (spec §T3).
- astro.config.ts: 7-rule `lbus-mdx-preflight` vite transform for vitepress
legacy syntax that mdx can't parse:
1. `layout:` → `docs_layout:` frontmatter (avoid astro-mdx module resolve
on values like "api-reference").
2. Strip `## Foo {#bar}` heading anchors.
3. Strip `<style scoped>...</style>` blocks.
4. Convert `<https://...>` autolinks to plain URLs.
5. Escape placeholder tags like `<id>`, `<token>` (HTML whitelist incl.
`<center>` preserved).
6. Strip leading `:` from vue-style JSX props (`:title=` → `title=`).
7. Strip `<!-- HTML comments -->`.
Also switched `remarkPlugins` from `mdx({...})` (deprecated in Astro 7) to
`markdown.remarkPlugins` and installed remark-heading-id (currently unused
since preflight strips anchor syntax; stage-2 will restore anchors via
rehype-slug or fix the plugin integration).
Verified: 884/884 dev URLs return 200. Astro check 0 errors.
Stage-2 blockers not addressed by this commit (see ledger):
- `bun run build:canary` still fails on screener_search.mdx:203 Go struct
literal inside a fenced code block (Astro 7 rolldown+oxc bug).
- Heading anchor ids are stripped; navigation to #hash fragments won't
scroll to the right place.
- BEM classNames should be codemodded to Tailwind utility classes.
- `<style scoped>` content is discarded; page-scoped CSS not migrated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stage-1 closure commit. The vitepress reference archive in .legacy/ has
served its purpose as a port reference through T2–T17 and is now removed.
Removed from package.json (not consumed by any src/scripts/packages code):
deps: @headlessui/vue, @jsonforms/core, @jsonforms/vue, @jsonforms/vue-vanilla,
@vue-flow/background, @vue-flow/core, @vueuse/core, floating-vue,
markdown-it, motion-v, reka-ui, shiki, vue-i18n
devDeps: @unocss/extractor-mdc, @unocss/transformer-variant-group,
markdown-it-container, markdown-it-mathjax3, sass-embedded,
unocss, vitepress, vitepress-plugin-group-icons,
vitepress-plugin-mermaid, vue
Not present in package.json (skipped cleanly): @vitejs/plugin-vue,
@vue/tsconfig, vue-tsc.
Added to .gitignore: dist-diff/, .baseline-dist/, .baseline-sitemap.xml
(opencli artifact dirs for boss's manual verification pass).
astro check: 0 errors. .vue files in src tree: 0. bun install: 23 packages
removed.
Boss's next step: run opencli url-diff / dom-diff / visual-diff against
vitepress baseline to confirm stage-1 URL parity.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add pagefind@^1 to dependencies (used as CLI in build:* scripts via bunx)
- SearchButton: hosts Cmd+K / Ctrl+K / '/' (with form-field guard) global
shortcuts; renders inline SVG search icon + ⌘K hint; manages open state
- SearchDialog: dynamic-imports /pagefind/pagefind.js at runtime with
@vite-ignore; falls back to "Search is available after first production
build" when import fails (dev env); debounced input (200 ms); locale
filtering on raw result URLs; ↑↓/Enter keyboard navigation; Esc closes;
backdrop click closes; body scroll locked while open; data-lbus-component
- SearchResults: renders listbox with active-index highlight; pagefind <mark>
excerpt via dangerouslySetInnerHTML; empty state via t(locale,'search.empty')
- TopNav: import SearchButton; insert <SearchButton locale={locale} /> before
<LanguageSwitcher /> in right-controls region
astro check: 5 pre-existing scripts/ errors, 0 new errors
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Region filter and remark plugin use picomatch directly. It was previously
a transitive dep of astro/vite/vitest, guarded by @ts-ignore. Declaring
it (plus @types/picomatch) removes the silent-break risk and eliminates
the type suppressions.
Ruling in
.superpowers/sdd/2026-08-17-astro-migration-stage-1/progress.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire src/content.config.ts to glob docs/{en,zh-CN,zh-HK}/**/*.mdx.
Implement resolveUrl/resolveLocale mirroring vitepress rewriteMarkdownPath
semantics; 16 unit tests cover index files, tri-lingual prefix, and
absolute/relative slug overrides.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire astro.config.ts, tsconfig paths, and a placeholder hello-world at
/ so `bun run dev` starts. Sitemap integration configured with tri-lingual
i18n; shikiConfig binds github-light/dark for later theme toggle.
Placeholder index.astro will be replaced by [...slug].astro in T5.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Move vitepress config, theme, postcss and unocss configs into
.legacy/vitepress-reference/ for use as a source-of-truth reference
while we port components to React. Astro dependencies not yet
installed; scripts entry-points point to astro but running them will
fail until T2 lands astro.config.ts and dependencies.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Runtime US tenant detection based on cookie `app_id` /
`x-original-app-id` (longbridge_us / longbridge_us_uat), aligned with
openapi-website-private.
- API baseUrl (request.ts) prefers US host when cookie matches:
longbridge_us -> https://mr.longbridge.com
longbridge_us_uat -> https://mr.longbridge-staging.com
falls back to hostname suffix.
- __API_PROXY_URL__ resolves the same way in an inline bootstrap script
that runs before longport-internal.iife.js loads.
- Append `with-us=1` to the login redirect URL.
- Pin longport-internal.iife.js to versioned CDN artifact that contains
US tenant normalization
(assets.lbctrl.com/openapi-sdk/release/longport-internal-202607201728.iife.js).
- Rename portal gateway host m.* -> mr.* in package.json,
region.config.ts,
config.mts and workflow yml.
## Merge Verdict
**[APPROVE]** — Region URL rewriting is now complete across every
artifact channel; global build verified untouched.
> 6 files · +73 / -26 · `docs/.vitepress` `scripts/` `package.json`
---
## Summary
- Extract a shared `buildRegionUrlReplacements()` in `region-utils.ts`
that emits **four** rules per non-default hostname (protocol-prefixed +
bare-text for both `siteHostname` and `apiBaseUrl`), and route the four
pre-existing rewrite sites (`region-filter.ts`, `transformHtml`,
`buildEnd install`, plus the new ones) through it.
- Add a new Vite `region-source-url-rewrite` plugin (`enforce: 'pre'`)
that rewrites hardcoded hostnames inside `.vue/.ts/.json/.yaml` source
modules — this is the only channel that reaches Vite-compiled JS bundles
(install command strings in Vue components, `mcp-tools.json` connect
links, `openapi.yaml` error-message text).
- Bring `.md` copies + `llms.txt` into region rewriting:
`normalize_md.ts` (the real writer of `dist/**/*.md`) and
`generate-llms.ts` (the static `llms-intro.md` injection point) now
share the same helper.
- Fix root-cause env-leak: `build:cn` now also passes `VITE_REGION=cn`
to the `bun run build:llms` segment — previously `cross-env` only scoped
to the `vitepress build` process, so `normalize_md`/`generate-llms`
never knew it was a CN build and silently produced `.com` artifacts.
---
## Risk Analysis
| Risk | Level | Mitigation |
|------|-------|-----------|
| Global build (`build:release`) accidentally rewritten | ✅ | Shared
helper returns `[]` when `VITE_REGION` is unset → every call site is a
no-op. Verified: `dist/longbridge-terminal/install.ps1` is
byte-identical to source; `mcp.html` keeps all 9 `.com`; `llms-full.txt`
keeps all 112 `open.longbridge.com`. |
| Bare-rule replacement double-matches | ✅ | `open.longbridge.com` is
**not** a substring of `openapi.longbridge.com` (5th char `.` vs `a`),
so the four rules are mutually independent regardless of order. |
| Vite `transform` runs on every module — performance hit | 🟢 |
`buildRegionUrlReplacements()` is lightweight (one env read + small
array build). On global builds it short-circuits via
`replacements.length === 0`. |
| `enforce: 'pre'` ordering vs `yaml-transform` | ✅ | `'pre'` plugins
run before normal plugins, so this transform sees raw YAML text and
rewrites it before `yaml-transform` JSON-stringifies it. |
| Hardcoded global hostnames in helper | 🟡 | Helper compares against the
literal `'https://open.longbridge.com'` /
`'https://openapi.longbridge.com'`. If the global domain ever changes,
this file plus `region.config.ts` must be updated together. Same
constraint already existed before this PR. |
---
## Design Decisions
- **Centralize rules in `region-utils.ts`** instead of inlining at four
call sites — four sites already drifted (HTML had two rules but markdown
had only the URL form before the previous PR). One source of truth
prevents future drift.
- **Pre-stage Vite transform** rather than a post-build dist scan —
keeps source maps intact and lets the rewrite participate in dependency
invalidation. It also naturally covers `openapi.yaml` (huge but fine —
string `split/join` is O(n) and only runs once per module per build).
- **Bare-hostname rules alongside URL rules** — covers
`[open.longbridge.com/connect](https://...)` markdown patterns where
only the link target gets matched by URL rules; the display text needs
the bare-host rule.
- **Source `install` / `install.ps1` keep `.com`** — global build's
`buildEnd` already had a rewrite pass; making source `.com`-default lets
the existing rewrite mechanism do the work and avoids two
source-of-truth files.
---
## Code Notes
1. **[Info]** `region-utils.ts:25` comment "first so bare rules don't
double-match"
In practice both orderings are correct because after either rule runs
the other one's "from" string no longer exists in the result. The note
is defensive rather than load-bearing.
— Author note: deferred to next iteration.
2. **[Info]** `config.mts` Vite transform hook calls
`buildRegionUrlReplacements()` per module
The helper is cheap but is invoked once per source module on every
build. Could be hoisted to the closure top if profiling ever flags it;
not worth the structural change today.
— Author note: deferred to next iteration.
3. **[Info]** `package.json` build:cn duplicates `cross-env
VITE_REGION=cn` across two segments
Maintainable but easy to forget if a third stage is added later. Could
be solved with `cross-env-shell` wrapping the whole chain, but that's a
separate cleanup.
— Author note: deferred to next iteration.
4. **[Needs review]** Vite `transform` regex includes `.yaml`/`.yml`
This is intentional — `openapi.yaml` ships hardcoded
`https://open.longbridge.com/sdk` and error-message URLs that must be
rewritten for CN. Reviewer should confirm there's no other YAML in the
dependency graph whose `.com` strings must be preserved as global
references. None observed in the current tree.
---
## Verification
- ✅ `bun run build:cn` succeeds; `rg -l
'(open|openapi)\.longbridge\.com' docs/.vitepress/dist` → **zero
residual `.com`** across HTML/MD/JS/scripts.
- ✅ `bun run build:release` succeeds; `install.ps1` and `install` are
byte-identical to source; `mcp.html` keeps 9× `.com`; `llms-full.txt`
keeps 112× `open.longbridge.com`; CN endpoint mentions inside docs
(`getting-started.md` etc.) are preserved as intended.
- ✅ `openapi-quote.longbridge.cn` / `openapi-trade.longbridge.cn` counts
in `getting-started.html` match source 1:1 (no over-rewrite).
- 📋 Reviewer to confirm: CN site (`open.longbridge.cn`) renders `mcp.md`
/ `skill/install` pages with the new URLs after deploy.
Co-authored-by: 袁昌瑞 <changrui.yuan@longbridge-inc.com>
Inject the `google-one-tap.es.js` CDN bundle into every page <head> via
VitePress `head` config, so it loads in both `bun run dev` and built
output (works around `transformHtml` being build-only).
- Gated by `VITE_REGION !== 'cn'` (Google unavailable in China).
- `data-proxy` attribute driven by `process.env.PROXY`: `canary` for
`dev:canary` / `build:canary`, otherwise `production`. CI inherits via
the same npm scripts — no workflow edits required.
- No `data-region` passed: the bundle falls back to `app-id` / `region`
cookies with `sg` default, which is correct for this first-party
`.longbridge.com` site.
- On success the bundle writes session cookies on `.longbridge.com` and
reloads, so `longportInternal.isLogin()` picks it up.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
## Summary
Implements a dedicated pricing page similar to
[financialdatasets.ai/pricing](https://www.financialdatasets.ai/pricing)
that visually compares different market data subscription levels and
their capabilities.
## Changes
- **`Pricing.vue`**: New Vue component that displays market data
subscription tiers in a pricing comparison table format, highlighting
feature availability and pricing across different permission levels
- **`index.ts`**: Registers the new `Pricing` component for use in
VitePress pages
- **`docs/en/docs/pricing.md`**: English pricing page
- **`docs/zh-CN/docs/pricing.md`**: Simplified Chinese pricing page
- **`docs/zh-HK/docs/pricing.md`**: Traditional Chinese pricing page
## Motivation
Users need a clear way to understand what market data features are
available at each subscription tier and how the tiers differ in price
and capability. A pricing comparison page makes it easy to evaluate
which subscription level fits their needs.
🤖 Auto-generated by Endless.
---------
Co-authored-by: Huacnlee Li Huashun <huacnlee@longbridge-inc.com>
Co-authored-by: Jason Lee <huacnlee@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary
- Promote MCP from a Docs sub-page to a top-level nav entry (URL stays
`/docs/mcp`)
- Add **Available tools** section: 100+ tools live-fetched at build time
from `openapi.longbridge.com/mcp/tools.json`, with search +
single-expand accordion + snapshot fallback
- Rewrite **Available capabilities** to 6 rows (realtime / fundamentals
/ derivatives / account / trading / automation)
- Bump CLI command count `65+` -> `120+`
All changes synced across `en` / `zh-CN` / `zh-HK`.
## Test plan
- [ ] Top nav shows MCP between CLI and API Reference; `/docs/mcp` has
no left sidebar
- [ ] Section order: Available capabilities -> Available tools ->
Prerequisites -> Client setup
- [ ] Search + expand works; only one tool expanded at a time;
out-of-view auto-scrolls inside the panel only
- [ ] `bun run build:canary` passes; snapshot fallback triggers on URL
sabotage
---------
Co-authored-by: 袁章洪 <zhanghong.yuan@longbridge-inc.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary
- Add `my_topics` and `create_topic` API reference pages in all 3
locales (en/zh-CN/zh-HK), using `<SDKLinks>` + `<Tabs>` pattern with
CLI, Python, Python async, Node.js, Java, Rust, C++, Go examples
- Rename Content sidebar category → **Community** across all locales
- Rename `security_topics` page title → "Get Community Topics by Symbol"
- Add Community content section to `cli.md` (all locales)
- Update `openapi.yaml`: add `x-codeSamples` for `list_my_topics` and
`create_topic`, fix `create_topic` schema (`title` not required at
schema level), move all community endpoints to `Community` tag
- Update CLI skill overview with `longbridge update` command details
## Key design notes
- `create_topic` page includes an article/post comparison table
explaining title requirement and body format differences
- SDK method signatures verified from source code
(`/Users/jason/work/openapi/`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary
- **New `/skill` page** with full trilingual support (en / zh-CN /
zh-HK)
- **Interactive chat demo**: 4 scenarios (Live Quote, Portfolio,
Subscription, Earnings) × 5 AI clients (OpenClaw, ChatGPT, Claude,
Claude Code, Codex) with typewriter animation
- **Rich response rendering**: tables with Shadcn-style borders, mini
SVG sparkline charts, syntax-highlighted code blocks
- **Scenario cards section** below the demo describing use cases
- **Nav update**: Skill link added to all three locale nav configs
## Bug Fixes
- **Syntax highlighter**: replaced chained-regex approach with a
single-pass tokenizer. The old code ran number → keyword → string
regexes sequentially on the same string — the keyword regex matched
`class` inside generated `<span class="hl-n">` attributes, and the
string regex matched `"hl-n"` as a string literal, corrupting the HTML
structure entirely
- **v-html routing**: replaced `currentMessages[2]?.rich` template
condition with an explicit `isRichResponse` ref set in `runAnimation()`
to prevent incorrect branch selection when switching client tabs
## Test plan
- [ ] Visit `/skill`, `/zh-CN/skill`, `/zh-HK/skill` — page loads in all
locales
- [ ] Click through all 4 scenario tabs — animation plays correctly for
each
- [ ] Click through all 5 client tabs — correct message shown, Claude
Code tab shows syntax-highlighted code (not raw HTML)
- [ ] Verify sparkline charts render in the Live Quote / OpenClaw
scenario
- [ ] Verify table borders render correctly (outer border + row
dividers, no missing bottom border)
- [ ] Verify gain/loss values show green/red colors in tables
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Summary
- Rename project from "OpenAPI" to "Longbridge Developers" across all
locales, config, and docs
- Add local SVG assets for homepage features (replacing external CDN
URLs)
- **TipContainer**: replace emoji with SVG icons, Shadcn-style
border/bg/text per type using `data-type` + CSS `@apply`
- **Tabs**: improved tab styling, fix `ul`/`ol` indentation in tab
content
- **Sidebar**: active item uses brand color background (color-mix) with
rounded corners
- **Doc footer**: LLMs Text + Edit this page on same row, subdued
colors, lighter "Updated at"
- **Breadcrumb**: remove primary color
- **UserMenu**: rename Profile → Dashboard (控制台)
- **Style**: inline code color fix, blockquote updates, font
improvements
## Test plan
- [ ] Verify rename appears correctly in EN / zh-CN / zh-HK locales
- [ ] Check homepage SVGs load from `/assets/`
- [ ] Check TipContainer all 6 types render with correct
border/bg/text/icon colors in light and dark mode
- [ ] Check Tabs styling and list indentation
- [ ] Check sidebar active item background
- [ ] Check doc footer layout (LLMs Text + Edit this page same row)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace localStorage-based session management with LongPort Internal
API calls
- Add LongPort Internal SDK script injection via head tag and Vite
plugin
- Create TypeScript definitions for Internal SDK interfaces (UserInfo,
Member, Setting, etc.)
- Refactor UserAvatar component to use Internal API for user info and
login state
- Add useAvatar composable for fetching user avatar from Internal API
- Update UserAvatarIcon to handle empty avatar state with fallback
styling
- Include SDK type definitions in TypeScript compilation
- Update Node.js engine requirement to >=24.0.0
- Remove @ts-ignore comments for Vite plugins
- Add Edit Link
Co-authored-by: 石石 <lei.yang@longbridge-inc.com>