Skip to content

fix(desktop): keep plain-text fences copyable - #49972

Open
s-c-h-a-f-e-r wants to merge 1 commit into
NousResearch:mainfrom
s-c-h-a-f-e-r:fix/desktop-copyable-text-fences
Open

fix(desktop): keep plain-text fences copyable#49972
s-c-h-a-f-e-r wants to merge 1 commit into
NousResearch:mainfrom
s-c-h-a-f-e-r:fix/desktop-copyable-text-fences

Conversation

@s-c-h-a-f-e-r

Copy link
Copy Markdown

Summary

  • Preserve explicit text, plain, and plaintext fenced blocks in the desktop markdown pipeline.
  • Prevent the language tag from leaking into visible prose as text Hallo....
  • Keep seller-message/email/prompt drafts in code-card style blocks with per-block copy buttons.

Verification

  • npm exec vitest run src/components/assistant-ui/markdown-text.test.ts -- --environment node
  • npm run typecheck

Note

  • npm run test:ui -- src/components/assistant-ui/markdown-text.test.ts is currently blocked in this checkout by a jsdom dependency ESM/CJS incompatibility in html-encoding-sniffer requiring @exodus/bytes/encoding-lite.js; the focused test passes under the node environment because this preprocessor test does not need jsdom.

@alt-glitch alt-glitch added type/bug Something isn't working comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have comp/desktop Electron desktop app (apps/desktop/*) and removed comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels Jun 21, 2026
@Kinkoolino-Hermes

Copy link
Copy Markdown
Contributor

Additional source-backed validation / reproduction:

CommonMark defines the text after an opening fence as the fence’s info string; the code block content starts on the subsequent lines. So when a fenced block explicitly uses the info string text, that text is metadata/language info, not rendered prose content.

I also checked #57540 and #57542. They describe the same text / plain / plaintext language-tag leak, and #57542 is already triaged as a duplicate of this earlier PR. We have seen the same failure mode in current Desktop/TUI-style rendering: the persisted Markdown is an explicit plain-text fenced block, but the desktop preprocessing path demotes it and the visible output starts with the language tag as text. That points to a renderer/preprocessor bug, not an agent-output issue.

Useful acceptance boundary:

  • explicit text, plain, and plaintext fences remain code blocks and copyable;
  • older persisted messages re-render correctly after the fix;
  • the prose-fence heuristic may still demote truly untagged prose-like fences, but should not demote explicit plain-text language tags.

This PR looks like the cleaner consolidation target versus #57542 because it is the earlier open PR for the same mechanism/symptom.

Source checked: CommonMark fenced code block semantics — https://spec.commonmark.org/0.31.2/#fenced-code-blocks

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tracing this through both markdown stages. The premise is verified on current main: apps/desktop/src/lib/markdown-code.ts:301-308 classifies explicit plain-text fences with prose bodies as prose, and apps/desktop/src/components/chat/shiki-highlighter.tsx:133-155 independently uses isLikelyProseCodeBlock() to choose a prose div instead of the copyable CodeCard.

Problems

  • The added tests cover only preprocessing and only text (apps/desktop/src/components/assistant-ui/markdown-text.test.ts:73-100). They do not protect the renderer-side isLikelyProseCodeBlock() change, nor the stated plain and plaintext variants.

Suggested changes

  • Add table-driven helper coverage in apps/desktop/src/lib/markdown-code.test.ts for text, plain, and plaintext, asserting prose content remains a code block.
  • Parameterize the closed and streaming preprocessing cases over those same tags.

Automated hermes-sweeper review.

expect(output).toContain('const value = 1;')
})

it('keeps explicit text fences copyable instead of leaking the language label into prose', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: parameterize this regression over text, plain, and plaintext, then add direct isLikelyProseCodeBlock() coverage. The renderer makes a separate copy-card decision through that helper (apps/desktop/src/components/chat/shiki-highlighter.tsx:133), so the current preprocessing-only text case does not cover the full stated contract.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants