From b27ce4a51b88d4d4473a3a7de26b042580df15fd Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Wed, 4 Nov 2026 15:24:24 -0800 Subject: [PATCH 1/2] fix(web): keep a document link inline in the coordinator's sentence --- apps/web/src/markdown.tsx | 13 +++++-- apps/web/src/pages/workspace-layout.css | 10 ----- .../web/src/pages/workspace/print-document.ts | 4 +- .../src/pages/workspace/transcript.test.tsx | 39 +++++++++++++++++++ 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/apps/web/src/markdown.tsx b/apps/web/src/markdown.tsx index 1f3b8307..4c87c69e 100644 --- a/apps/web/src/markdown.tsx +++ b/apps/web/src/markdown.tsx @@ -73,15 +73,20 @@ function inline( const href = match[5].replace(MARKS, ""); if (href.startsWith(DOCUMENT_LINK) && openDocument !== undefined) { const id = decodeURIComponent(href.slice(DOCUMENT_LINK.length)); + // A link rather than a button: a button never breaks across lines, so a + // title in a sentence would jump to its own line and shed its punctuation. out.push( - , + , ); } else if (href.startsWith("https://") || href.startsWith("http://")) { out.push( diff --git a/apps/web/src/pages/workspace-layout.css b/apps/web/src/pages/workspace-layout.css index 7b5278f6..793c0be6 100644 --- a/apps/web/src/pages/workspace-layout.css +++ b/apps/web/src/pages/workspace-layout.css @@ -145,19 +145,9 @@ } .artifact-mention { - display: inline-flex; - align-items: center; - min-height: 44px; - min-width: 44px; - margin: 0; - padding: 0 var(--space-1); - border: 0; - background: transparent; color: var(--wb-foreground); - font: inherit; text-decoration: underline; text-underline-offset: 0.15em; - cursor: pointer; } .artifact-mention:focus-visible { diff --git a/apps/web/src/pages/workspace/print-document.ts b/apps/web/src/pages/workspace/print-document.ts index b3c2760d..d3d3f7a6 100644 --- a/apps/web/src/pages/workspace/print-document.ts +++ b/apps/web/src/pages/workspace/print-document.ts @@ -1,6 +1,6 @@ const SHEET_CLASS = "print-sheet"; -// Controls and selection affordances stay on screen; a document mention prints as its text. -const SCREEN_ONLY = ".pane-bar, .passage-offer, button:not(.artifact-mention)"; +// Controls and selection affordances stay on screen. +const SCREEN_ONLY = ".pane-bar, .passage-offer, button"; /** * Prints the document a pane shows through the browser's dialog, whose "Save as PDF" diff --git a/apps/web/src/pages/workspace/transcript.test.tsx b/apps/web/src/pages/workspace/transcript.test.tsx index 48550793..ddc39e96 100644 --- a/apps/web/src/pages/workspace/transcript.test.tsx +++ b/apps/web/src/pages/workspace/transcript.test.tsx @@ -6,6 +6,7 @@ import { maxTurnText, } from "@corbits-builder/schemas/conversation"; import { wording } from "../../config.ts"; +import { Markdown } from "../../markdown.tsx"; import { DocumentDecision } from "./document-decision.tsx"; import { awaitingCommit, @@ -96,6 +97,44 @@ describe("conversation transcript", () => { expect(html).not.toContain("sha256"); }); + test("a bold document name stays a link inside its sentence and still opens", () => { + const source = + "Already written into **Ways to build your list**. What the app does is next."; + const documents = [{ id: "approaches", title: "Ways to build your list" }]; + const html = renderToStaticMarkup( + {}} + />, + ); + // A link, not a button: a button is one unbreakable box, so the title would leave the line. + expect(html).toContain( + 'into Ways to build your list. What', + ); + + const opened: string[] = []; + const link = findElement( + Markdown({ + source: "See [Ways to build your list](document:approaches).", + openDocument: (id) => opened.push(id), + }), + "a", + ); + let prevented = false; + ( + link?.props["onClick"] as + | ((event: { preventDefault(): void }) => void) + | undefined + )?.({ + preventDefault: () => { + prevented = true; + }, + }); + expect(opened).toEqual(["approaches"]); + expect(prevented).toBe(true); + }); + test("an approval asks about the document in words", () => { const html = renderToStaticMarkup( Date: Wed, 4 Nov 2026 15:44:57 -0800 Subject: [PATCH 2/2] fix(web): open a document mention without a placeholder href --- apps/web/src/markdown.tsx | 16 +++++---- .../src/pages/workspace/transcript.test.tsx | 33 +++++++++---------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/apps/web/src/markdown.tsx b/apps/web/src/markdown.tsx index 4c87c69e..2ade90b1 100644 --- a/apps/web/src/markdown.tsx +++ b/apps/web/src/markdown.tsx @@ -73,20 +73,24 @@ function inline( const href = match[5].replace(MARKS, ""); if (href.startsWith(DOCUMENT_LINK) && openDocument !== undefined) { const id = decodeURIComponent(href.slice(DOCUMENT_LINK.length)); - // A link rather than a button: a button never breaks across lines, so a - // title in a sentence would jump to its own line and shed its punctuation. + // An inline span rather than a button: a button never breaks across lines, + // so a title in a sentence would jump to its own line and shed its + // punctuation. No href, since a document has no address to open elsewhere. out.push( - { + onClick={() => openDocument(id)} + onKeyDown={(event) => { + if (event.key !== "Enter" && event.key !== " ") return; event.preventDefault(); openDocument(id); }} > {label} - , + , ); } else if (href.startsWith("https://") || href.startsWith("http://")) { out.push( diff --git a/apps/web/src/pages/workspace/transcript.test.tsx b/apps/web/src/pages/workspace/transcript.test.tsx index ddc39e96..1ff0f1e7 100644 --- a/apps/web/src/pages/workspace/transcript.test.tsx +++ b/apps/web/src/pages/workspace/transcript.test.tsx @@ -97,7 +97,7 @@ describe("conversation transcript", () => { expect(html).not.toContain("sha256"); }); - test("a bold document name stays a link inside its sentence and still opens", () => { + test("a bold document name stays inline in its sentence and opens by click or key", () => { const source = "Already written into **Ways to build your list**. What the app does is next."; const documents = [{ id: "approaches", title: "Ways to build your list" }]; @@ -108,31 +108,30 @@ describe("conversation transcript", () => { onOpenDocument={() => {}} />, ); - // A link, not a button: a button is one unbreakable box, so the title would leave the line. + // Inline, not a