mirror of
https://github.com/longbridge/developers.git
synced 2026-09-19 03:34:09 +08:00
0add508ae2
## 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>
118 lines
3.7 KiB
Python
118 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
import re
|
|
import json
|
|
import requests
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
DOCS = ROOT / 'docs'
|
|
LOCALES = ['en', 'zh-CN', 'zh-HK']
|
|
|
|
METHOD_RE = re.compile(r'HTTP Method</td><td>\s*([^<\n]+)', re.I)
|
|
URL_RE = re.compile(r'HTTP URL</td><td>\s*([^<\n]+)', re.I)
|
|
TITLE_RE = re.compile(r'^title:\s*(.+)$', re.M)
|
|
|
|
|
|
def collect(locale):
|
|
rows = []
|
|
for p in (DOCS / locale / 'docs').rglob('*.md'):
|
|
txt = p.read_text(encoding='utf-8')
|
|
m = METHOD_RE.search(txt)
|
|
u = URL_RE.search(txt)
|
|
if not (m and u):
|
|
continue
|
|
title = (TITLE_RE.search(txt).group(1).strip().strip('"\'') if TITLE_RE.search(txt) else p.stem)
|
|
rows.append({
|
|
'locale': locale,
|
|
'method': m.group(1).strip().upper(),
|
|
'path': u.group(1).strip(),
|
|
'title': title,
|
|
'file': str(p.relative_to(ROOT)).replace('\\', '/'),
|
|
})
|
|
return rows
|
|
|
|
|
|
def check_endpoint(base, method, path):
|
|
url = base.rstrip('/') + path
|
|
try:
|
|
r = requests.request(method, url, timeout=5)
|
|
return {'status': r.status_code, 'code': None, 'message': None}
|
|
except Exception as e:
|
|
return {'status': None, 'error': str(e)}
|
|
|
|
|
|
def main():
|
|
all_rows = []
|
|
for lc in LOCALES:
|
|
all_rows.extend(collect(lc))
|
|
|
|
by_key = defaultdict(list)
|
|
for r in all_rows:
|
|
by_key[(r['method'], r['path'])].append(r)
|
|
|
|
keys = sorted(by_key.keys())
|
|
|
|
# locale parity
|
|
missing = []
|
|
for k in keys:
|
|
exists = {x['locale'] for x in by_key[k]}
|
|
for lc in LOCALES:
|
|
if lc not in exists:
|
|
missing.append({'method': k[0], 'path': k[1], 'missing_locale': lc})
|
|
|
|
# runtime existence check (non-404 means route exists)
|
|
runtime = []
|
|
for method, path in keys:
|
|
prod = check_endpoint('https://openapi.longbridge.com', method, path)
|
|
test = check_endpoint('https://openapi.longbridge.com', method, path)
|
|
runtime.append({
|
|
'method': method,
|
|
'path': path,
|
|
'prod_status': prod.get('status'),
|
|
'test_status': test.get('status')
|
|
})
|
|
|
|
out = {
|
|
'total_unique_http_apis': len(keys),
|
|
'apis': runtime,
|
|
'locale_missing': missing,
|
|
}
|
|
|
|
out_dir = ROOT / 'openapi' / 'audit'
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
(out_dir / 'api-audit.json').write_text(json.dumps(out, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
|
|
|
|
md = []
|
|
md.append('# API Docs Audit')
|
|
md.append('')
|
|
md.append(f'- Total unique HTTP APIs in docs: **{len(keys)}**')
|
|
md.append(f'- Locale parity gaps: **{len(missing)}**')
|
|
md.append('')
|
|
md.append('## Runtime route check (non-404 = endpoint exists)')
|
|
md.append('')
|
|
md.append('| Method | Path | openapi.longbridge.com | openapi.longbridge.com |')
|
|
md.append('| --- | --- | --- | --- |')
|
|
for r in runtime:
|
|
md.append(f"| `{r['method']}` | `{r['path']}` | `{r['prod_status']}` | `{r['test_status']}` |")
|
|
md.append('')
|
|
if missing:
|
|
md.append('## Locale parity gaps')
|
|
md.append('')
|
|
md.append('| Method | Path | Missing Locale |')
|
|
md.append('| --- | --- | --- |')
|
|
for m in missing:
|
|
md.append(f"| `{m['method']}` | `{m['path']}` | `{m['missing_locale']}` |")
|
|
else:
|
|
md.append('## Locale parity gaps')
|
|
md.append('')
|
|
md.append('No locale gaps found for currently documented HTTP APIs.')
|
|
|
|
(out_dir / 'api-audit.md').write_text('\n'.join(md) + '\n', encoding='utf-8')
|
|
print('wrote', out_dir / 'api-audit.json')
|
|
print('wrote', out_dir / 'api-audit.md')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|