Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
aa91ee9
chore(web): markdown micro-benchmark over real replies and PR comments
juliusmarminge Oct 30, 2026
c65aeff
feat(web): a fast Markdown parser builds remark's tree, and static bl…
juliusmarminge Oct 30, 2026
a0dd0ef
feat(web): Markdown without raw HTML skips remark-rehype and the rehy…
juliusmarminge Oct 30, 2026
37b98d5
feat(web): raw HTML in Markdown nests without parse5 for the tags rev…
juliusmarminge Oct 30, 2026
0b226b8
feat(web): the fast Markdown path is the default; VITE_T3_MARKDOWN_PA…
juliusmarminge Oct 30, 2026
a620a1b
test(web): the fast Markdown path builds remark's and rehype's trees …
juliusmarminge Oct 30, 2026
cf7e64c
perf(web): Markdown links and file chips share one tooltip, and stati…
juliusmarminge Oct 30, 2026
81abecd
feat(web): the fast Markdown parser handles the chat's math syntax, s…
juliusmarminge Oct 30, 2026
7f390ed
perf(web): Markdown positions are shared, text direction skips buildi…
juliusmarminge Oct 30, 2026
e3d47a2
perf(web): offscreen pull request comments skip layout and paint
juliusmarminge Oct 30, 2026
69c9fb6
perf(web): closed Markdown details mount Base UI only when hovered or…
juliusmarminge Oct 30, 2026
2425744
perf(web): code block buttons share the message's tooltip, which clos…
juliusmarminge Oct 30, 2026
a0452a6
perf(web): finished messages scan for file links once, and raw HTML r…
juliusmarminge Oct 30, 2026
6bc1520
perf(web): a streamed reply splits into blocks once per update and lo…
juliusmarminge Oct 30, 2026
4b68f97
Merge remote-tracking branch 'origin/main' into t3/wasm-markdown-rend…
juliusmarminge Oct 31, 2026
a3bc49e
style(web): format markdownTextDirection
juliusmarminge Oct 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"fast-xml-parser": "5.7.3",
"hast-util-to-html": "^9.0.5",
"hast-util-to-jsx-runtime": "^2.3.6",
"hastscript": "^9.0.1",
"heic-to": "^1.5.2",
"html-url-attributes": "^3.0.1",
"jose": "catalog:",
Expand All @@ -73,6 +74,7 @@
"morphicons": "^1.7.1",
"pdfjs-dist": "6.4.299",
"pele": "^1.8.0",
"property-information": "^7.2.0",
"react": "catalog:",
"react-dom": "catalog:",
"react-markdown": "^10.1.0",
Expand Down Expand Up @@ -106,6 +108,7 @@
"@vercel/config": "^0.3.0",
"@vitejs/plugin-react": "^6.1.2",
"@vitest/browser-playwright": "5.0.5",
"commonmark-spec": "0.31.2",
"compression": "^1.8.2",
"micromark-util-types": "catalog:",
"oxc-transform-react": "~0.152.0",
Expand Down
49 changes: 44 additions & 5 deletions apps/web/src/components/ChatMarkdown.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ const { workspaceSearch, filesystemMetadata } = vi.hoisted(() => ({
filesystemMetadata: vi.fn(),
}));
const parsedSources = vi.hoisted(() => [] as string[]);
vi.mock(import("mdast-util-from-markdown"), async (original) => {
vi.mock(import("../markdown/parseMarkdown"), async (original) => {
const actual = await original();
return {
...actual,
fromMarkdown: ((source: string, ...rest: never[]) => {
parsedSources.push(String(source));
return (actual.fromMarkdown as (...args: unknown[]) => unknown)(source, ...rest);
}) as typeof actual.fromMarkdown,
parseMarkdown: (source, options) => {
parsedSources.push(source);
return actual.parseMarkdown(source, options);
},
};
});

Expand Down Expand Up @@ -201,6 +201,45 @@ describe("ChatMarkdown favicon privacy", () => {
});
});

describe("ChatMarkdown link tooltips", () => {
it("shows each link's address on one shared tooltip as the pointer moves between links", async () => {
setup();
await using screen = await render(
<ChatMarkdown
cwd="/tmp/project"
text="[First](https://example.com/a) and [Second](https://example.org/b)"
/>,
);
await userEvent.hover(screen.getByRole("link", { name: /First/ }));
await expect.element(page.getByText("https://example.com/a")).toBeVisible();
await userEvent.hover(screen.getByRole("link", { name: /Second/ }));
await expect.element(page.getByText("https://example.org/b")).toBeVisible();
expect(document.querySelectorAll('[data-slot="tooltip-popup"]')).toHaveLength(1);
});
});

describe("ChatMarkdown details", () => {
it("opens collapsed details by click, and by keyboard without losing focus", async () => {
setup();
await using screen = await render(
<ChatMarkdown
cwd="/tmp/project"
text={
"<details><summary>First</summary>\n\nHidden one\n\n</details>\n\n<details><summary>Second</summary>\n\nHidden two\n\n</details>"
}
/>,
);
await userEvent.click(screen.getByRole("button", { name: "First" }));
await expect.element(screen.getByText("Hidden one")).toBeVisible();
await userEvent.tab();
const second = screen.getByRole("button", { name: "Second" });
await expect.element(second).toHaveFocus();
await userEvent.keyboard("{Enter}");
await expect.element(screen.getByText("Hidden two")).toBeVisible();
await expect.element(screen.getByRole("button", { name: "Second" })).toHaveFocus();
});
});

describe("ChatMarkdown streaming", () => {
it("runs only a complete single-line shell block after a click", async () => {
setup();
Expand Down
Loading
Loading