1074 Commits

Author SHA1 Message Date
拉罐 8745180638 fix(auth): 登录跳转暂时注释掉 with-us 参数 (#1261)
同步更新 createLoginRedirectPath 的 JSDoc 示例链接。

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 16:07:28 +08:00
Hogan a1e35a0bd8 docs(quote): refresh option Greek field descriptions on Calc Index (#1241)
Update the delta / gamma / theta / vega / rho response-field
descriptions on the Calc Index (calc_indexes) interface across all three
languages (zh-CN / zh-HK / en).

Key change: **theta now documents the per-day semantics** — the raw
value has been divided by 365 on the server to convert to a daily value
(previously the docs incorrectly said divide by 100). delta/gamma get
full descriptions; vega/rho reworded to the /100 form.

Source of truth: the 期权希腊字母 spec table. Matches the SDK
(longbridge/openapi#586), MCP, and CLI changes.
2026-09-16 15:00:59 +08:00
拉罐 2edde7ac61 Fix/service (#1258)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-14 16:55:16 +08:00
Hogan cb5678d84a docs(trade): document the page parameter on history_executions (#1255)
## Problem

The `history_executions` docs (zh-CN + en) list only `symbol` /
`start_at` / `end_at` in the parameter table, and the response section
states "`has_more` is `true` when results exceed 1000" — but never
document **how** to page. The sibling `all_executions` doc does document
`page`. So a reader hitting the 1000-record cap has no documented way to
paginate.

This surfaced downstream in longbridge/longbridge-terminal#320, where a
consumer building a ledger assumed windowed queries silently truncate at
1000 with no way forward.

## Change

Add the `page` parameter row to `history_executions.mdx` (zh-CN + en),
copied from `all_executions`.

**Verified against the production gateway**: `page` is 1-based and
respected — on an account with N (<1000) fills, `page=1` returns all N
and `page=2` returns empty; per-page size is fixed at 1000 (no size
param).

Refs longbridge/longbridge-terminal#320
2026-09-14 15:52:09 +08:00
Hogan 2e18485a28 docs(market): fix Java getRankList example to use RankListOptions (#1250)
Fixes #1249.

The Java example for the rank-list (人气排行榜) API called
`ctx.getRankList("hot_all-us", false)` with positional args, but the
Java API takes a `RankListOptions` object:

```java
RankListOptions opts = new RankListOptions();
opts.key = "hot_all-us";
opts.needArticle = false;
var resp = ctx.getRankList(opts).get();
```

Updated the Java example in all three languages (en / zh-CN / zh-HK).
The Rust / Python / Node.js / C++ examples in the same page were already
correct.

The accompanying runtime crash (`JNI call failed`) is fixed separately
in longbridge/openapi#587.
2026-09-14 15:52:00 +08:00
拉罐 a50d356db8 Fix/doc drap (#1239)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 18:05:35 +08:00
拉罐 dd00a96c9f Feat/fix cn nav pipeline (#1237)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-01 19:27:47 +08:00
拉罐 f01ca6579d fix(ci): scope CN build region and isolate OSS uploads (#1236)
让 CN 构建的区域变量覆盖完整构建链,确保导航按 CN 规则生成。
移除 CN 产物写入全局 OSS 路径的命令,避免跨区域资源覆盖。

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-01 19:03:41 +08:00
twosugar 98153c730f fix(search): 搜索索引改从 /assets/ 提供,修复线上索引 404
根目录 .json 落到 nginx catch-all 被改写为 <path>/index.html 而 404
("Search index failed to load")。将 search-index endpoint 移到 /assets/
(nginx _assets.conf 直服),fetch 路径同步更新,对齐 _astro/assets 处理,不动 nginx。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-31 10:21:42 +08:00
twosugar b88557fe38 docs: 同步老站(main)遗漏的内容改动
审计 main 领先本分支的 14 个提交,其余均已在新站,补齐缺失的 3 处:
- agent-auth: 授权码有效期 5 分钟 → 10 分钟(#1232,三语言)
- mcp: 工具数 100+ → 160+(#1225,三语言)
- Pricing: A 股实时行情加"仅限中国大陆 IP、其他地区延迟 15 分钟"注释(#1229,三语言)
  并给 .pricing-matrix-feat 加 white-space: pre-line 使注释换行

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-31 10:21:42 +08:00
twosugar 0423063662 fix(sidebar): 层级对齐、hover 仅变色、嵌套引导线、导航保持展开与滚动
- 叶子与分组头统一 px-2;顶层 section 子级去 pl-3,与一级图标列左对齐(对齐老站)
- hover 去背景色,仅文字变品牌色(叶子 hover:text,分组头 group-hover)
- 深层嵌套子级 ul 加左侧引导竖线
- transition:persist 保留桌面侧边栏岛,导航不再收起/闪动/偏移;
  响应式 pathname 同步 active、active 组自动展开
- astro:before-swap/after-swap 存取 scrollTop,修复 persist 重挂载导致的滚动跳顶

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 20:03:30 +08:00
twosugar 6d5b92f7a3 fix(search): 弹窗 portal 指向活动 document.body,修复导航后搜索无反应
SearchDialog 的 portal 目标在挂载时缓存了 document.body。TopNav 是
transition:persist(只挂载一次),而 ClientRouter 每次导航替换 <body>,
缓存指向了已脱离文档的旧 body,导致软导航后点击搜索弹窗渲染到不可见处。
改为渲染时直接读取活动 document.body(isOpen 初始为 false,SSR 安全)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 20:03:30 +08:00
twosugar f5dfacadaa fix(deploy): 资源目录改为 /assets 对齐 nginx,修复样式全 404
Astro 默认把哈希 CSS/JS 输出到 /_astro/,但线上 nginx 只有 /assets/ 的规则
(老站 VitePress 的资源路径),/_astro/* 落到 catch-all 被改写成 .../index.html
→ 全部 404,样式与 hydration 全崩。设 build.assets='assets' 复用现有 _assets.conf,
不动 nginx、对齐老站。public/assets/sdk.svg 名字唯一,与哈希文件共存无冲突。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 17:58:06 +08:00
twosugar 3c28e8734c fix(build): mcp-tools.json 在 config:setup 预生成,修复 astro check 2026-08-28 17:42:24 +08:00
twosugar 2843b64766 fix: 补 @types/markdown-it 依赖 2026-08-28 17:26:28 +08:00
twosugar 3bb2aa327d fix(ci): bun.lock 内网 registry 改回公共 yarnpkg,对齐老项目 2026-08-28 17:18:08 +08:00
twosugar 482b92a197 ci(build): 用 frozen-lockfile + 依赖缓存,并加 verbose 定位 bun install 卡顿
CI 裸跑 bun install 曾卡 20+ 分钟且日志看不到进度(bun 进度条走 \r 不实时刷新)。
四个 install 步骤改为 bun install --frozen-lockfile --verbose:frozen 确定性安装、
lockfile 不符即快速失败;verbose 非 TTY 逐行输出,便于定位卡住的包。
前置 actions/cache 缓存 ~/.bun/install/cache(按 bun.lock 哈希),命中后基本不走网络。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 16:49:14 +08:00
twosugar 43a386ca92 chore(lint): 引入 oxlint 并清理未使用代码
新增 oxlint@1.80.0 + .oxlintrc.json + lint/lint:fix 脚本;清空所有
no-unused-vars 告警(未用 React/导入/变量/参数),删除无引用的 ui/Skill。
oxlint exit 0。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 16:14:18 +08:00
twosugar 3a99087698 perf(build): copy-routes 按 nginx 拓扑去重,产物 410M→218M
原脚本对每条路由双写 foo.html + foo/index.html,近半数为字节相同的死文件。
改为按线上 nginx 前缀规则只保留所需形态(/docs·/sdk→扁平 .html,其余→
目录 /index.html)并删除死文件。nginx 解析模拟 938 route、0 MISSING。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 16:14:18 +08:00
twosugar b81c0d43a8 fix(seo): 恢复页面标题品牌名(移植 VitePress createTitle)
BaseLayout 原样输出 title,全站每页丢了 "Longbridge Developers":内容页丢
后缀、首页丢前缀。新增 lib/site-title.ts 作唯一组装点,三处 slug 统一调用,
同步修正 og:title / twitter:title。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 16:14:18 +08:00
twosugar fe87ad2fb3 fix(mobile): 修复 pricing/skill 页小屏横向溢出
pricing-cycle 计费周期控件(4 项)+ 标签在手机端超出视口:小屏改为
标签上移、min-width:0 可收缩、缩 tab 内边距、overflow-x:auto 兜底。
skill "Choose your AI tool" 卡片因 white-space:pre 命令块撑宽:网格轨道
改 minmax(0,1fr)、卡片/cmd min-width:0、code overflow-x:auto 框内滚动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 16:14:18 +08:00
twosugar 2cdddf342f fix(build): bidirectional copy-routes, css minify, sitemap filter & CI dist path
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>
2026-08-28 15:04:41 +08:00
twosugar df0db9a8df feat(migrate): port QuantChart indicator chart (replace stub)
Faithful React+SVG port of the legacy QuantChart.vue (macd/rsi/bb/ema/stoch) with the baked-in sample series; renders SSR-safe. Re-point the MDX map to it and drop the placeholder stub.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 15:04:02 +08:00
twosugar fd4f154f98 feat(migrate): restore platform head scripts, login bridge, analytics, whale app & SEO
Port the legacy config.mts head layer + theme/index.ts enhanceApp side effects that the migration dropped: apiProxyBootstrap + longport-internal login bridge, Google Analytics, __LB_PROXY__, 神策 sensors SDK + init (pageview/click heatmap/stay/member binding), iconfont, Google One-Tap, and Helora support-widget boot. Add saveInviteCodeFromUrl (cookie capture on load + navigation) and a route progress bar (site-init). Whale-app (in-app WebView): UA detection hides the top nav / local nav / footer and takes the theme from lbtheme/<mode> (no-flash inline + ThemeToggle guard). Per-page SEO head: canonical + hreflang alternates + og/twitter + text/markdown alternate. CN region hides the Pricing/Docs nav items and the Features menu. Also render mermaid fences client-side and restore the theme-toggle circular view-transition.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-28 15:03:49 +08:00
twosugar ebc9ed3628 feat(migrate): align shell/docs with legacy, add mobile responsive & login redirect
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>
2026-08-28 13:40:03 +08:00
twosugar b38e88f63c feat(migrate): replicate homepage & skill pages 1:1 with legacy
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>
2026-08-28 13:39:19 +08:00
twosugar 7a3daba71c feat(migrate): sync upstream main into Astro and align footer with legacy
Content (from origin/main, 3 locales):
- add grid trading, multi-leg, CLI ai/serve, and style-guide pages (58 files)
- rename Longbridge AI -> LongbridgeAI; add multi-leg fields, remove
  contract_size, add US paper-account notes (39 files)
- sync grid trading endpoints into openapi.yaml

Components:
- footer: add AI Integrations column (MCP / ChatGPT App / Claude Connector);
  restore legacy tagline, status pill, padding, grid, heading + link styles;
  hug footer to content (drop mt-16)
- sdk-links: support grid module (GridContext, hideGo)
- raw markdown: serve EN source prefixless at /docs/<path>.md (legacy parity)

Polish:
- CliCommand terminal card with per-command doc deep-links
- resolve relative-slug URLs in sidebar / prev-next / breadcrumb
- hydrate pricing billing toggle + eyebrow badge; thin scrollbars
- global body base + site-wide antialiasing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 15:56:05 +08:00
twosugar b90cbb4eb4 feat(docs): align doc detail page with legacy (footer, callout, meta, anchors, TOC, sidebar)
- 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>
2026-08-25 19:45:38 +08:00
twosugar 73aaa4abc1 fix(migrate): docs bold text weight 600 (was browser-default 700)
Legacy `.vp-doc strong` is 600. Our bold text used the browser/Preflight
default (bolder → 700). Added `.docs-content strong, b { font-weight: 600 }`.
Verified: **Quote** etc. now compute 600.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 19:03:45 +08:00
twosugar 23d933de76 feat(migrate): docs content font → self-hosted Inter (match legacy .vp-doc)
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>
2026-08-24 18:57:07 +08:00
twosugar ef9263c1aa feat(migrate): align docs links, lists, blockquote, headings, hr, img
Second content-parity pass (after code blocks + tables). Values measured
from legacy `.vp-doc` via getComputedStyle.

- **Links** — were `color: var(--lbus-c-brand)` (an undefined token, so
  links rendered as plain black text with no underline). Legacy `.vp-doc a`
  is body-colored + underlined + 500 weight (NOT brand-tinted). Set
  `color: inherit; text-decoration: underline; font-weight: 500`, hover →
  brand. Reset breadcrumb + prev/next links (also inside .docs-content) back
  to no-underline so the prose-link treatment doesn't leak into chrome.
- **Lists** — Tailwind Preflight had stripped markers; restored
  `list-style: disc/decimal`, 20px indent, 16px block margin, 4px li margin
  (legacy values).
- **Blockquote** — was `--lbus-c-brand` border + undefined `--lbus-c-text-
  muted` color. Legacy: 2px --lbus-c-divider bar, --lb-fg-2 italic text.
- **Headings** — explicit 600 weight (Preflight resets weight); removed the
  h2 border-bottom (legacy has none) and its undefined --lbus-c-border;
  section margins h2 3rem / h3 2rem / h4 1.5rem; h1 mt 0.
- **hr / img** — hr: 1px --lbus-c-divider rule, 24px margin; img: max-width
  100%, height auto.

Verified on /docs/getting-started: prose link rgb(60,60,67) underline 500
(= legacy) while breadcrumb links stay plain; ul disc / ol decimal at 20px;
blockquote 2px rgba(0,0,0,.06) muted italic; h2 24/600 no border mt48; h3
20/600 mt32.

Remaining: callouts (`:::` — already via remarkCallout, needs a parity
check) and `<CliCommand>` blocks. Next pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 18:26:07 +08:00
twosugar 1579a396a7 fix(migrate): soften docs body text to legacy tone (was too black)
Boss: table (and generally the article) text looked blacker than legacy.
Root cause is global, not table-specific: legacy `.vp-doc` colors all
content — paragraphs, table cells, headings — with --vp-c-text-1 = #3c3c43
(a soft near-black). Our content inherited --lb-fg-1 (#0A0E19, near pure
black), so everything read darker.

Scoped the softer tone to `.docs-content`:
- `.docs-content { color: #3c3c43 }` (light) / `rgba(255,255,245,0.86)`
  (dark), matching legacy --vp-c-text-1.
- Headings and `td` changed to `color: inherit` (were pinned to the darker
  --lbus-c-text / --lb-fg-1) so they pick up the softer body color.
- `th` keeps the muted --lb-fg-2 (matches legacy header rgb(103,103,108)).

Site chrome (nav/sidebar) is untouched — it still uses the --lb-* tokens.
Verified on /docs/quote/objects: p / td / h1 / h2 all rgb(60,60,67) =
legacy #3c3c43; th rgb(108,110,117).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 18:17:54 +08:00
twosugar 5a784b5c28 feat(migrate): align docs code blocks + tables with legacy .vp-doc
First pass of the content-style parity work (code blocks + tables — the two
widest-reach elements: 267 and ~300 pages). Values measured from legacy
production `.vp-doc` via getComputedStyle.

Code blocks (`.docs-content pre`, shiki `pre.astro-code`):
- radius 6→8, padding 12/16→20/24, font 16→13px, margin 16/0 — matching
  legacy. Shiki emits an inline white bg for github-light; overridden to
  --lb-bg-2 (≈ legacy #f6f6f7) via `html:not([data-mode=dark])` so it only
  affects light — the dark bg (#24292e) keeps flowing through shiki.css's
  existing `[data-mode=dark] .astro-code` swap. Inline `code` now gets the
  legacy chip style (bg --lb-bg-2, radius 4, 0.875em, JetBrains Mono).

Tables (previously UNSTYLED — Tailwind Preflight had stripped all borders):
- block + overflow-x auto (horizontal scroll), collapsed 1px dividers in
  --lbus-c-divider (= legacy rgba(0,0,0,.06)), header + even-row zebra in
  --lb-bg-2, 8/16 cell padding, header 600-weight muted (--lb-fg-2), body
  cells --lb-fg-1 — matching legacy.

Also replaces the previously-broken references to the undefined tokens
--lbus-c-bg-soft / --lbus-c-border in the old pre/code rules with real
--lb-* tokens.

Verified light + dark on /docs/quote/objects: code bg #f3f5f6 (light) /
#24292e (dark); table borders rgba(0,0,0,.06) (light) / rgba(255,255,255,
.06) (dark); header + zebra --lb-bg-2 both modes.

Remaining content elements (links, blockquote, images, callouts, nested
lists, hr) still use plain/undefined styling — next passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 17:58:44 +08:00
twosugar 734ec686e3 fix(migrate): render page title as article H1 when body has none
Boss: the article H1 (e.g. "Overview") was missing. Legacy renders the
page title as the H1 at the top of the article. The docs convention splits:
155 pages start with their own body `#` heading (VitePress shows that), but
pages like index.mdx and quote/objects.mdx have no body `#` — their
frontmatter title becomes the H1. Our DocsLayout rendered neither an
injected title nor (for those pages) any H1, so the title vanished.

DocsLayout now injects `<h1>{entry.data.title}</h1>` after the breadcrumb
ONLY when the body has no level-1 heading (`headings.some(depth === 1)` is
false). Verified no body H1 uses the `{#id}` syntax, so the headings array
is reliable for the check and there's no risk of a double H1.

Verified:
- /docs → H1 "Overview" (injected)
- /docs/quote/objects → H1 "Definition" (injected)
- /docs/quote/overview → H1 "Quote API Overview" (its own body H1; the
  title "Overview" is NOT injected — no duplicate)

The injected H1 sits inside .docs-content (picks up the 2rem article inset
and the 2rem font-size); the DOM-derived TOC only scans h2/h3 so it is
unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 17:46:31 +08:00
twosugar a9f67b4ad5 fix(migrate): align sidebar items with the header logo
Boss: the left sidebar menu should line up with the header logo. The
header is a centered `max-w-[1240px] px-6 mx-auto` container (logo at
container-left + 24px), but the full-width docs layout put the sidebar
flush at the viewport edge (items at ~16px) — misaligned by ~108px at
1440.

- `.docs-layout` (lg) gains `padding-left: max(0px, calc((100vw - 1240px)
  / 2))`, matching the header container's left edge, so the sidebar column
  starts where the header content does.
- Sidebar aside padding `px-4` → `px-6`, matching the header's px-6, so the
  item pill's left edge lands at container-left + 24 = the logo.

Legacy does the same (sidebar link left == logo left, both 404 at 2000px).
Verified at 1440: sidebar pill left 124 == logo left 124 (delta 0); item
icon 16px right, mirroring legacy. Content region still fills to the right
(wide article) and the wash stays scoped to the content region.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 17:38:46 +08:00
twosugar ca6ee1d5a7 fix(migrate): docs layout — full-width content region + wash scoped to it
Two boss findings:
1. The top wash bled under the sidebar/menu column.
2. On wide screens the whole layout was capped at 80rem and centered in the
   viewport, so the article got squeezed into the middle with big empty
   margins on both sides — legacy keeps the sidebar flush-left and lets the
   content region fill the rest.

Restructured to the legacy two-level model:
- `.docs-layout` is now a full-width grid: `16rem minmax(0,1fr)` (sidebar |
  content region). No more centered 80rem block.
- New `.docs-body` wraps the content region; the wash moved to
  `.docs-body::before` so it covers only the area right of the sidebar,
  never under the menu.
- New `.docs-inner` caps the article + TOC at 80rem and centers them within
  the post-sidebar space (the legacy `.VPDoc .container` behavior): article
  fills, TOC 14rem, 4rem gutter between them.
- Article/breadcrumb offsets preserved (body padding-left 2rem, breadcrumb
  margin-left -2rem → breadcrumb hangs 32px left of the body).

DocsLayout.astro wraps <article> + <TOC> in `.docs-body > .docs-inner`.

Verified at 1440px: sidebar 0–256 (flush left), content region 256–1440,
article text ~800px wide (was ~640, squished), left/right gutters 64px,
wash present on the content region only (sidebar column stays white). On
wider viewports the inner caps at 1280 and centers in the post-sidebar
space, matching legacy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 17:29:23 +08:00
twosugar f863a2cd06 fix(migrate): docs content margins, breadcrumb offset, TOC vertical position
Boss review against legacy production (measured at 1440px):
- article gutters were 32px each side vs legacy 64px
- breadcrumb sat 21px RIGHT of the body text; legacy has it ~32px LEFT
- TOC "On this page" was flush at the top (65px); legacy sits ~108px, level
  with the article body, not the breadcrumb

Changes to docs.css:
- `.docs-content` gets `padding: 0 2rem`. Combined with the grid's 2rem gap
  this restores ~64px gutters between the article and the sidebar / TOC.
- The breadcrumb gets `margin: 0 -2rem`, pulling it back out of that padding
  to the column's left edge — so it hangs 32px left of the indented body,
  matching legacy.
- `.docs-toc` uses `margin-top: 2.75rem` (not padding) to drop "On this
  page" down level with the article start while still sticking near the top
  on scroll; the divider border moves to the inner `nav` so the line begins
  at the title rather than in the margin gap above it.

Verified at 1440px: left gutter 64, right gutter 64, breadcrumb 32px left
of body, On-this-page top 105 (legacy 108).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 17:12:59 +08:00
twosugar 7afd0e1bd5 feat(migrate): docs TOC divider + subtle top wash
Two docs-page polish items from boss review:

- **TOC divider** — the "On this page" column had no separator from the
  article. Legacy production draws it on `.VPDocAsideOutline .content` as
  `border-left: 1px solid rgba(0,0,0,.06)` — exactly our --lbus-c-divider.
  Added `border-left` + `padding-left: 1.5rem` to `.docs-toc`.

- **Top wash (晕染)** — a subtle brand-tinted radial gradient at the top of
  the docs page, echoing the homepage hero. Implemented as
  `.docs-layout::before` (absolute, out of the grid flow; grid children
  lifted with z-index) — `radial-gradient(ellipse 90% 60% at 50% 0%,
  color-mix(--lb-brand 12%, transparent), transparent 72%)`, 420px tall.
  NOTE: legacy docs pages have no such wash (verified against production),
  so this is a new effect per request — intensity (currently 12%) is easy
  to dial.

Verified on /docs: TOC shows the left divider; a gentle mint glow sits
behind the top of the content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 16:51:40 +08:00
twosugar b31e2daed9 fix(migrate): TOC dropped headings with custom {#id} anchors
Boss: the right-hand "On this page" was missing items (Rate Limit,
Pricing) vs legacy. Those two headings use the vitepress custom-id syntax
`## Rate Limit {#rate-limit}`. The vite preflight in astro.config.ts
(step 2) rewrites `## Foo {#bar}` into a raw JSX `<h2 id="bar">…</h2>`
element before the markdown pipeline runs (MDX/acorn chokes on the `{…}`
otherwise). Astro's heading collector walks the mdast for `heading`
nodes, so JSX-element headings never enter the `headings` export — they
render on the page but vanish from the TOC.

Derive the TOC from the rendered DOM instead of trusting the server
`headings` prop: on mount (and on each astro:page-load) scan
`.docs-content h2, h3` for elements with an id. This captures every
rendered heading regardless of how it was produced (markdown, JSX-rewrite,
or component). The prop still seeds the SSR render so there's server HTML;
the DOM reconciles the full set after hydration.

Verified: /docs TOC now lists all 7 sections including Rate Limit and
Pricing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 13:51:14 +08:00
twosugar 4e1e210e2c fix(migrate): sidebar sub-section headers not bold
Boss: the nested collapsible headers (Subscribe / Stocks / Options /
Warrants / Analytics / Watchlist, Fundamentals / Market Data, …) were
font-medium, reading as semi-bold. Only top-level section headers (Quote /
Fundamental / Market / …) should be bold. Dropped nested sub-section
headers to font-normal, matching their sibling leaf links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 13:46:41 +08:00
twosugar 1d8e3f61e5 perf(migrate): SPA-style navigation via Astro ClientRouter (no full reload)
Boss: switching docs pages did a full document reload — white flash and
visible jank — whereas the legacy VitePress site navigated client-side.
Astro is MPA by default; each nav reloaded the whole page.

Add Astro's ClientRouter (View Transitions): same-origin navigations are
now intercepted and the document is swapped client-side (no white flash,
smooth fade). Three cross-cutting caveats handled:

- **Theme flash** — a swap replaces <html>'s attributes with the new
  document's SSR default (data-mode "light"), so a dark-mode user would
  flash light on every nav. The head theme script now defines applyTheme()
  and registers it on `astro:after-swap` (fires before paint); the listener
  lives on `document` so it is not duplicated per swap. Verified: dark stays
  dark across client navs (body bg #0A0E19, no flash).

- **Shell persistence** — TopNav & Footer get `transition:persist` keyed by
  locale (`nav-${locale}` / `footer-${locale}`): the shell stays mounted
  within a locale (no re-hydrate flicker, theme-toggle keeps its state) but
  re-mounts when the locale changes so labels/links update. Because the
  persisted TopNav's `pathname` prop goes stale, TopNav now tracks the path
  in state and refreshes it on `astro:page-load` (also closes the mobile
  menu) — active highlight + LanguageSwitcher stay correct.

- **Per-page script re-run** — tabs-hydrate ran once on DOMContentLoaded, so
  Tabs on client-swapped pages were never wired up. Switched to
  `astro:page-load` (fires on initial load + every swap) with a
  `data-tabsHydrated` idempotency guard. Verified: 7-tab page hydrates after
  a client nav.

Sidebar/TOC are intentionally NOT persisted so their active state + scope
(docs vs cli) recompute per page; they swap client-side (no white flash).

Verified in browser: a window sentinel survives navigation (client swap,
not full reload); URL updates; sidebar + top-nav active states update; dark
theme persists; Tabs hydrate; console clean (no hydration errors).

KNOWN FOLLOW-UP: the non-persisted sidebar resets its scroll position on
each nav (legacy preserved it). Persisting it needs after-swap active/
auto-expand/scope re-sync — deferred to a separate change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 12:06:37 +08:00
twosugar 296299f594 fix(migrate): remove empty band above docs sidebar/content
Boss: legacy has no gap between the header and the first sidebar item /
breadcrumb. `.docs-layout` had `padding: 2rem 1rem`, and since the header
is sticky (in flow, 60px) the 2rem (32px) top padding pushed the whole
grid down — leaving a full-width empty band between the nav and the first
sidebar row / breadcrumb.

Dropped the top padding (`padding: 0 1rem 2rem`). Columns now sit flush
under the header with only their own small insets (sidebar py-6 = 24px,
breadcrumb ~14px, TOC flush). Measured: header bottom 61 → first sidebar
item 85, breadcrumb 75, TOC 61.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 11:55:04 +08:00
twosugar 9916dfeffb fix(migrate): ThemeToggle hydration mismatch (server MoonIcon vs client SunIcon)
Switching pages threw "Hydration failed because the server rendered HTML
didn't match the client" pointing at ThemeToggle's icon. `isDark` called
matchMedia('(prefers-color-scheme: dark)') during render: on the server
(and thus in the SSR HTML) window is undefined so isDark was false →
MoonIcon; on a dark-preferring machine the client's first render (which
must byte-match the server) evaluated matchMedia → true → SunIcon, so the
trees diverged and React regenerated the island on every navigation.

Gate the theme-dependent icon behind a `mounted` flag (false during SSR
and the first client render, set true in useEffect). Both sides now render
MoonIcon initially — matching HTML — then the effect resolves the real
theme and swaps to the correct icon. Light-mode users see no change;
dark-mode users get a one-frame Moon→Sun swap after mount.

Verified: navigating /docs/getting-started → /docs/llm shows a clean
console (no hydration error), only the benign vite + buffer-externalized
notices.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 11:26:00 +08:00
twosugar 4e845950a8 fix(migrate): sidebar double-highlight — root Overview lit on every /docs/*
After adding the root "Overview" (/docs) entry, isActiveNode matched a
link with `pathname === link || pathname.startsWith(link + '/')`. Since
"/docs" is a path prefix of every docs page, "/docs/getting-started"
satisfied `startsWith("/docs/")`, so BOTH Overview and Getting Started
rendered the active teal pill.

A link is active only on its exact page; the prefix branch is removed.
Group activeness is still derived from descendants (node.items.some),
which is unaffected — Quote/Stocks still auto-expand on a nested active
page.

Verified: /docs/getting-started now highlights only "Getting Started";
Overview is inactive. Each of the two sidebar mounts (desktop sticky +
mobile drawer) has exactly one active row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 11:18:18 +08:00
twosugar fd6b5a6996 fix(migrate): restore root "Overview" sidebar entry (docs/{locale}/index.mdx)
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>
2026-08-24 11:14:51 +08:00
twosugar 7b095c193d refactor(migrate): rebuild docs sidebar to match legacy vitepress style
Boss reference (image #27, legacy master): the astro sidebar (image #26)
was a flat gray list — no group dividers, no bold section headers, icons
never rendered, collapse non-interactive, wrong active state. Rebuilt to
reproduce the legacy VPSidebar look.

- **`sidebar-icons.ts`** (new) — the 22 lucide 16×16 SVG fragments copied
  verbatim from legacy docs/.vitepress/theme/utils/gen.ts SIDEBAR_ICONS,
  keyed by the `sidebar_icon` frontmatter / `_category_.json` icon value.
  Fragments use stroke=currentColor so the row's text color drives them.

- **`SidebarItem.tsx`** — was rendering icons as an empty `data-icon` span
  (glyphs never appeared) and the group `<button>` had no onClick (collapse
  was dead). Rewritten:
    · Leaf link: rounded-lg px-4 py-1, optional icon (fg-3 / brand when
      active), active = teal text + `color-mix(in oklab, --lb-brand 10%,
      transparent)` pill (matches legacy `.is-active .VPLink`), hover =
      brand text + bg-2.
    · Group: interactive collapse via useState seeded from active/collapsed.
      level-0 header is bold --lb-fg-1 with icon; deeper sub-section headers
      are medium --lb-fg-2. Caret is a chevron that points right when
      collapsed and rotates 90° to point down when open (legacy .caret-icon).
    · Active group auto-expands (open seeds true when a descendant matches).

- **`Sidebar.tsx`** — coalesces the flat top-level node list into groups:
  consecutive leaf links bundle into one header-less block, each section
  node stands alone. Groups after the first get a top divider
  (border-t --app-card-stroke + 10px gap), reproducing legacy `.group +
  .group` separators.

Verified in browser: /docs shows grouped sidebar with dividers, bold
Quote/Fundamental/Market headers + bar-chart/book-open/line-chart icons +
down-chevrons; /docs/quote/objects shows "Definition" with the teal active
pill and Quote auto-expanded — matching the legacy reference.

KNOWN GAP (data layer, not touched here): the root "Overview" (/docs,
docs/en/docs/index.mdx, sidebar_position -999) is absent from the top of
the first group because buildItems() in navigation.ts skips every
index.mdx. Legacy shows it as the first, active item. Flagged for a
separate data-layer fix pending confirmation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 10:55:16 +08:00
twosugar 0ab585eece feat(migrate): real search — build-time JSON index + minisearch + legacy UI
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>
2026-08-19 15:45:59 +08:00
twosugar 8f1cb30df7 fix(migrate): search dialog was clipped inside the header via portal
Boss's screenshot: clicking the search input opened a dialog that was
confined to the top nav bar strip, not a global overlay. The backdrop
also did not cover the page.

Root cause is a subtle CSS containing-block rule: any ancestor with
`filter`, `backdrop-filter`, `transform`, `perspective`, `will-change`
or `contain` becomes the containing block for `position: fixed`
descendants — so those descendants are NOT positioned relative to
the viewport but to that ancestor's box.

TopNav.tsx sets `[backdrop-filter:saturate(180%)_blur(20px)]` on the
<header> (added last round for legacy parity). SearchDialog was
rendered as a child of SearchButton, which lives inside <header>, so
its `fixed inset-0` backdrop resolved to "cover the 60px header" — not
"cover the viewport."

Fix: React portal SearchDialog to `document.body`, escaping the
backdrop-filter ancestor. Guarded with a `useEffect` that captures
`document.body` after mount so SSR does not touch `document`.
Behavior otherwise unchanged.

Verified: click search → global backdrop covers full viewport, dialog
centered at top:pt-20, everything else darkened.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-19 14:53:32 +08:00
twosugar 6cdbcc1b86 fix(migrate): docs sidebar was covering the 60px sticky header
Boss's screenshot: on /docs pages the left sidebar stretched from
top:0 to bottom, painting over the entire nav bar (Features / Pricing
/ Skill / CLI / MCP / Docs). Root cause was a dead-CSS + wrong-utility
combination:

- `src/styles/docs.css` defined `.docs-sidebar { position: sticky;
  top: 3.5rem; ...}` and `.docs-toc { top: 3.5rem; ...}`, but neither
  Sidebar.tsx nor TOC.tsx ever applied those classNames. The rules
  were dead code.
- Sidebar.tsx used `fixed inset-y-0 left-0 z-30 w-64 ... lg:relative
  lg:translate-x-0`. On lg+ the `lg:relative` won for `position`, but
  `inset-y-0` (top:0 + bottom:0) stayed active — stretching the aside
  from viewport top down over the sticky 60px header.

Fix:

- **`Sidebar.tsx`** — className rewritten so the two viewports carry
  their own contract:
    Mobile: fixed inset-y-0 left-0 z-40 w-64 (full-height drawer,
    z-40 so it covers the sticky header when the mobile menu opens,
    matching legacy AppSidebar.vue).
    Desktop (lg+): sticky top-[60px] inset-y-auto h-[calc(100vh-60px)]
    z-auto translate-x-0 (a column that begins BELOW the header and
    never reaches it). Added py-6 px-4 to restore the legacy sidebar
    padding.

- **`LocalNav.tsx`** — was `sticky top-14` (56px = old h-14 header
  guess). Bumped to `top-[60px]` so mobile local-nav sits flush under
  the 60px header instead of leaving a 4px gap.

- **`TOC.tsx`** — added the `docs-toc` className so the CSS ruleset
  in docs.css actually takes effect. Also bumped its `top: 3.5rem` /
  `max-height: calc(100vh - 3.5rem)` values in docs.css to 60px to
  match the header.

- **`docs.css`** — dead `.docs-sidebar` sticky ruleset removed. The
  sidebar's positioning now lives inline in Sidebar.tsx where the
  Tailwind utilities can't be silently overridden by an unused CSS
  block.

Verified: navigating to /docs shows the left sidebar starting under
"Home / Overview" breadcrumb (below the 60px header), and the top
nav — Features / Pricing / Skill / CLI / MCP / Docs — is fully
visible. TOC (ON THIS PAGE) also starts under the header on the right.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-19 14:49:09 +08:00
twosugar 0728588525 feat(migrate): port Features dropdown from legacy FeaturesMenu.vue
Boss called out: Features dropdown was a stub — clicking did nothing.
Ported the full nine-tile dropdown from legacy.

- **`src/data/features-menu.ts`** — verbatim data copy from legacy
  FeaturesMenu.vue lines 14-155: the same nine tiles (Live Market
  Quotes, Price History, Financial Statements, Analyst Estimates,
  Company News, SEC Filings, Institutional, Options & Warrants, Order
  Execution) with lucide-style icon path fragments, deep links into
  the /docs/ tree, and the en / zh-CN / zh-HK translations kept in
  lockstep. `localePath(locale, path)` mirrors legacy `useLocalePath`.

- **`src/components/shell/FeaturesMenu.tsx`** — React port with the
  same behavior contract as the vue original: mouse-enter opens the
  panel, mouse-leave closes it on a 150ms timer (matches legacy
  cursor-gap tolerance so users can traverse to the panel without it
  vanishing), click toggles for keyboard/tap parity, outside-click and
  Escape dismiss. Icons rendered via `dangerouslySetInnerHTML` inside
  a lucide 24×24 viewBox — same technique legacy used with `v-html`.
  Panel: `w-[640px]`, `grid-cols-3 gap-1`, `rounded-xl border p-2` with
  `--lb-shadow-menu`. Tokens migrated from `--vp-*` → `--lb-*` /
  `--app-*` (semantic aliases). Trigger reuses the exact nav-link
  spec (13.5px / 500 / 6·12 · fg-1@78%) and turns teal + rotates
  chevron 180° when open, matching legacy `.is-active` state.

- **`TopNav.tsx`** — swap the Features stub button for
  `<FeaturesMenu locale={locale} />`. Shares the same `client:load`
  hydration boundary as the surrounding TopNav island (Astro hydrates
  the whole React subtree, no extra directive needed).

Verified via chrome-devtools: click flips aria-expanded to "true",
menu with 9 menuitem children renders, screenshot matches boss's
reference image #21 tile-for-tile (icon, title, desc, layout).

NOTE: this session's dev server needed a manual `rm -rf
node_modules/.vite` + restart to clear a "504 Outdated Optimize Dep"
that Astro's HMR would not resolve on its own once a new file entered
the module graph — recorded here so the next surprise hydrate failure
after adding a component knows what to try first.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-19 14:33:02 +08:00