diff --git a/ui/src/components/IssueChatThread.test.tsx b/ui/src/components/IssueChatThread.test.tsx index 8be26a9f74..2625ecaa49 100644 --- a/ui/src/components/IssueChatThread.test.tsx +++ b/ui/src/components/IssueChatThread.test.tsx @@ -90,9 +90,9 @@ vi.mock("../lib/issue-chat-scroll", async (importOriginal) => { }); vi.mock("./MarkdownBody", () => ({ - MarkdownBody: ({ children }: { children: ReactNode }) => { - markdownBodyRenderMock(children); - return
{children}
; + MarkdownBody: ({ children, className }: { children: ReactNode; className?: string }) => { + markdownBodyRenderMock({ children, className }); + return
{children}
; }, })); @@ -354,6 +354,48 @@ describe("IssueChatThread", () => { }); }); + it("uses accent-safe markdown color in the current user's blue message bubble", () => { + const root = createRoot(container); + + act(() => { + root.render( + + {}} + showComposer={false} + enableLiveTranscriptPolling={false} + /> + , + ); + }); + + expect(markdownBodyRenderMock).toHaveBeenCalledWith(expect.objectContaining({ + children: "1. **Readable** markdown on blue", + className: expect.stringContaining("paperclip-markdown-on-accent"), + })); + + act(() => { + root.unmount(); + }); + }); + it("labels operator-interrupted cancelled runs as interrupted while preserving plain cancelled runs", () => { const root = createRoot(container); const linkedRuns: IssueChatLinkedRun[] = [ diff --git a/ui/src/components/IssueChatThread.tsx b/ui/src/components/IssueChatThread.tsx index ec3534f4c0..3aafbf53be 100644 --- a/ui/src/components/IssueChatThread.tsx +++ b/ui/src/components/IssueChatThread.tsx @@ -721,9 +721,7 @@ const IssueChatTextPart = memo(function IssueChatTextPart({ text, recessed, onAc } return ( { + it("inherits the current message bubble foreground for prose text, links, and list counters", () => { + const block = cssBlock(".paperclip-markdown.paperclip-markdown-on-accent"); + + expect(block).toContain("color: inherit"); + expect(block).toContain("--tw-prose-body: currentColor"); + expect(block).toContain("--tw-prose-links: currentColor"); + expect(block).toContain("--tw-prose-counters: currentColor"); + expect(block).toContain("--tw-prose-bullets: currentColor"); + expect(block).toContain("--tw-prose-invert-links: currentColor"); + }); + + it("keeps ordered-list markers and rendered link variants readable on accent bubbles", () => { + expect(cssBlock(".paperclip-markdown.paperclip-markdown-on-accent li::marker")).toContain( + "color: currentColor", + ); + expect(cssBlock(".paperclip-markdown.paperclip-markdown-on-accent :where(a, a:visited)")).toContain( + "color: currentColor", + ); + expect(cssBlock(".paperclip-markdown.paperclip-markdown-on-accent .paperclip-workspace-file-link")).toContain( + "color: currentColor", + ); + expect( + cssBlock( + ".paperclip-markdown.paperclip-markdown-on-accent :where(a.paperclip-mention-chip, a.paperclip-project-mention-chip)", + ), + ).toContain("color: currentColor !important"); + }); +}); diff --git a/ui/src/index.css b/ui/src/index.css index d749022899..f86dd53ba9 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -1042,12 +1042,49 @@ a.paperclip-mention-chip[data-mention-kind="agent"]::before { background-color: #ffffff0f; } +.paperclip-markdown.paperclip-markdown-on-accent :not(pre) > code { + background-color: color-mix(in oklab, currentColor 24%, transparent); + color: currentColor; +} + .paperclip-markdown { color: var(--foreground); font-size: 0.9375rem; line-height: 1.6; } +.paperclip-markdown.paperclip-markdown-on-accent { + color: inherit; + --tw-prose-body: currentColor; + --tw-prose-headings: currentColor; + --tw-prose-lead: currentColor; + --tw-prose-links: currentColor; + --tw-prose-bold: currentColor; + --tw-prose-counters: currentColor; + --tw-prose-bullets: currentColor; + --tw-prose-hr: color-mix(in oklab, currentColor 24%, transparent); + --tw-prose-quotes: currentColor; + --tw-prose-quote-borders: color-mix(in oklab, currentColor 56%, transparent); + --tw-prose-captions: color-mix(in oklab, currentColor 84%, transparent); + --tw-prose-code: currentColor; + --tw-prose-th-borders: color-mix(in oklab, currentColor 34%, transparent); + --tw-prose-td-borders: color-mix(in oklab, currentColor 22%, transparent); + --tw-prose-invert-body: currentColor; + --tw-prose-invert-headings: currentColor; + --tw-prose-invert-lead: currentColor; + --tw-prose-invert-links: currentColor; + --tw-prose-invert-bold: currentColor; + --tw-prose-invert-counters: currentColor; + --tw-prose-invert-bullets: currentColor; + --tw-prose-invert-hr: color-mix(in oklab, currentColor 24%, transparent); + --tw-prose-invert-quotes: currentColor; + --tw-prose-invert-quote-borders: color-mix(in oklab, currentColor 56%, transparent); + --tw-prose-invert-captions: color-mix(in oklab, currentColor 84%, transparent); + --tw-prose-invert-code: currentColor; + --tw-prose-invert-th-borders: color-mix(in oklab, currentColor 34%, transparent); + --tw-prose-invert-td-borders: color-mix(in oklab, currentColor 22%, transparent); +} + .paperclip-markdown > :first-child { margin-top: 0; } @@ -1087,6 +1124,10 @@ a.paperclip-mention-chip[data-mention-kind="agent"]::before { color: var(--muted-foreground); } +.paperclip-markdown.paperclip-markdown-on-accent li::marker { + color: currentColor; +} + .paperclip-markdown h1, .paperclip-markdown h2, .paperclip-markdown h3, @@ -1115,6 +1156,10 @@ a.paperclip-mention-chip[data-mention-kind="agent"]::before { font-size: 0.95rem; } +.paperclip-markdown.paperclip-markdown-on-accent :where(h1, h2, h3, h4, strong, b) { + color: currentColor; +} + .paperclip-markdown :where(strong, b) { color: var(--foreground); font-weight: 600; @@ -1127,6 +1172,16 @@ a.paperclip-mention-chip[data-mention-kind="agent"]::before { cursor: pointer; } +.paperclip-markdown.paperclip-markdown-on-accent :where(a, a:visited) { + color: currentColor; + text-decoration-color: color-mix(in oklab, currentColor 72%, transparent); +} + +.paperclip-markdown.paperclip-markdown-on-accent :where(a:hover, a:focus-visible) { + color: currentColor; + text-decoration-color: currentColor; +} + .paperclip-markdown a.paperclip-mention-chip { text-decoration: none; } @@ -1154,6 +1209,10 @@ a.paperclip-mention-chip[data-mention-kind="agent"]::before { color: color-mix(in oklab, var(--foreground) 80%, #58a6ff 20%); } +.dark .paperclip-markdown.paperclip-markdown-on-accent :where(a, a:visited) { + color: currentColor; +} + .paperclip-markdown blockquote { margin-left: 0; padding-left: 0.95rem; @@ -1161,6 +1220,27 @@ a.paperclip-mention-chip[data-mention-kind="agent"]::before { color: var(--muted-foreground); } +.paperclip-markdown.paperclip-markdown-on-accent blockquote { + border-left-color: color-mix(in oklab, currentColor 56%, transparent); + color: color-mix(in oklab, currentColor 88%, transparent); +} + +.paperclip-markdown.paperclip-markdown-on-accent .paperclip-workspace-file-link { + border-color: color-mix(in oklab, currentColor 34%, transparent); + background-color: color-mix(in oklab, currentColor 16%, transparent); + color: currentColor; +} + +.paperclip-markdown.paperclip-markdown-on-accent .paperclip-workspace-file-link:hover { + background-color: color-mix(in oklab, currentColor 24%, transparent); +} + +.paperclip-markdown.paperclip-markdown-on-accent :where(a.paperclip-mention-chip, a.paperclip-project-mention-chip) { + border-color: color-mix(in oklab, currentColor 34%, transparent) !important; + background: color-mix(in oklab, currentColor 16%, transparent) !important; + color: currentColor !important; +} + .paperclip-markdown hr { margin: 1.25rem 0; border-color: var(--border);