From c7d95f5b7f08222f728b4419f1b0a835cd4a75ae Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 30 Jul 2026 21:46:05 +0800 Subject: [PATCH] fix(web-read-card): align read spec with real schema and correct grammar docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Top-level read samples in read-card.spec use the read tool's real file_path schema key (web_fetch keeps its own path key); the Input-JSON assertion follows. The LANGS/LANG_ALIASES JSDoc no longer claims the TypeScript grammar embeds JSX/TSX — shiki's TS grammar tokenizes JSX approximately, an accepted trade to keep one JS-family boot grammar. --- .../ui-conversation/tests/read-card.spec.tsx | 10 +++++++--- .../ui-primitives/src/markdown/highlight.ts | 16 ++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/client/ui-conversation/tests/read-card.spec.tsx b/packages/client/ui-conversation/tests/read-card.spec.tsx index a4ead14748..d5804b505b 100644 --- a/packages/client/ui-conversation/tests/read-card.spec.tsx +++ b/packages/client/ui-conversation/tests/read-card.spec.tsx @@ -25,7 +25,11 @@ afterEach(cleanup) const SID = 's1' as SessionId -const ARGS = '{"path":"src/a.ts","offset":41}' +// The read tool's real schema key is `file_path`; the top-level read samples +// use it so the row exercises a production-shaped call. `web_fetch` (below) has +// its own schema whose key is not `file_path`, so it keeps a `url`-less `path`. +const ARGS = '{"file_path":"src/a.ts","offset":41}' +const WEB_FETCH_ARGS = '{"path":"src/a.ts","offset":41}' /** The read block's rendered content cells, one string per row (highlighting * breaks a line across token spans, so match on the row's textContent). */ @@ -122,7 +126,7 @@ describe('GenericToolCard read body', () => { expect(CHAT_READ_MAX_LINES).toBeLessThan(16) // web_fetch lands on the read variant without its own keyed row, so the // fallback card owns the resident read block. - const view = render() + const view = render() expect(view.container.querySelector('[data-read]')).not.toBeNull() expect(contentTexts(view.container)).toContain('export const a = 1') // The gutter keeps the file's own line numbers. @@ -256,7 +260,7 @@ describe('DetailsPanel Output section (read)', () => { const view = mount(snapshot({ nodes: [settled({ resultView: resultRead({ lines: long, totalLines: 20 }) })], }), target) - expect(view.getByText(/"path"/)).toBeTruthy() + expect(view.getByText(/"file_path"/)).toBeTruthy() expect(view.container.querySelector('[data-read]')).not.toBeNull() // The panel takes the primitive's own default cap (16), not the row's. expect(view.getByText(`… 其余 ${20 - 16} 行`)).toBeTruthy() diff --git a/packages/client/ui-primitives/src/markdown/highlight.ts b/packages/client/ui-primitives/src/markdown/highlight.ts index 2116fa2b93..d0727c833a 100644 --- a/packages/client/ui-primitives/src/markdown/highlight.ts +++ b/packages/client/ui-primitives/src/markdown/highlight.ts @@ -32,9 +32,12 @@ type LangModule = { default: typeof langTs } /** * Grammars the singleton loads at boot; each entry's own `name` is the id - * `codeToTokens`/`codeToHtml` resolve. The TypeScript grammar embeds JS/JSX/TSX, - * so the JS-family fence aliases resolve to it rather than a separate grammar. - * The read card's wider set loads lazily through {@link LAZY_GRAMMARS}. + * `codeToTokens`/`codeToHtml` resolve. The JS-family aliases (js/jsx/ts/tsx) + * resolve to the TypeScript grammar rather than a separate one: it tokenizes + * plain TS/JS exactly, and JSX/TSX approximately (shiki's TS grammar is not the + * dedicated TSX grammar, so JSX elements tokenize imperfectly) — an accepted + * trade to keep the boot set to one JS-family grammar. The read card's wider + * set loads lazily through {@link LAZY_GRAMMARS}. */ const LANGS = [langTs, langBash, langJson] @@ -80,9 +83,10 @@ const LAZY_GRAMMARS = new Map Promise>([ * inherited property and crashing the renderer inside shiki. Keys cover both * the markdown-fence aliases `CodeBlock` uses and the file-extension hint ids * the read tool's `langFromPath` emits, so both callers resolve the same - * grammars. The JS family maps to the TypeScript grammar (which embeds it), - * unchanged from when this was the only non-shell/JSON grammar. A value not in - * {@link LANGS} names a {@link LAZY_GRAMMARS} entry loaded on first use. + * grammars. The JS family maps to the TypeScript grammar (see {@link LANGS} for + * the JSX/TSX approximation), unchanged from when this was the only + * non-shell/JSON grammar. A value not in {@link LANGS} names a + * {@link LAZY_GRAMMARS} entry loaded on first use. */ const LANG_ALIASES = new Map([ ['typescript', 'typescript'],