Skip to content

Code block copy and save buttons in chat do not work #81

@Ovaculos

Description

@Ovaculos

Summary

The copy and save/download buttons rendered on formatted code blocks in the chat UI do not function. Clicking them has no visible effect — no clipboard write, no file download, no feedback state.

Repro

  1. Start a conversation and ask the agent to produce a code block (e.g., "write a Python hello world").
  2. Hover over the rendered code block — copy and save buttons appear.
  3. Click the copy button.
  4. Observe: clipboard is not updated (paste confirms nothing was written).
  5. Click the save/download button.
  6. Observe: no file download is triggered.

Root cause

Code blocks are rendered by the <Streamdown> component (web/src/components/MessageList.tsx:317), which provides its own built-in copy and download buttons on code fences. These buttons are part of the library's default rendering and are not wired to any custom handlers in the NimbleBrain integration.

The custom CopyButton component in MessageList.tsx:32 operates at the message level (copies the full raw markdown text of the message), not at the individual code block level. It is a separate button from the ones Streamdown renders inside each code block.

The Streamdown code block buttons likely rely on browser clipboard and download APIs that either:

  • Require a user gesture in a Secure Context, which may not be satisfied in the current render path, or
  • Are broken by the way the Streamdown component is mounted (e.g., inside an iframe, shadow DOM, or focus-trapped container that intercepts events)

No error handling exists anywhere in the copy flow — navigator.clipboard.writeText() calls are fire-and-forget with no try/catch (MessageList.tsx:36, ToolAccordion.tsx:218). Silent failures leave users with no feedback.

What would help

  • Determine whether Streamdown's built-in buttons work in isolation (outside NimbleBrain's layout)
  • If the issue is library-level: replace Streamdown's default code block rendering with a custom component that calls navigator.clipboard.writeText() directly with proper error handling and user feedback
  • If the issue is context-level (iframe / CSP / Secure Context): ensure the chat panel is served in a context where clipboard and download APIs are permitted
  • Add try/catch around all navigator.clipboard.writeText() calls and display a visible error state when the write fails

Key files

  • web/src/components/MessageList.tsx:317–327 — Streamdown instantiation (code block rendering)
  • web/src/components/MessageList.tsx:32–51 — message-level CopyButton (separate from code block buttons)
  • web/src/components/ToolAccordion.tsx:213–242 — tool result CopyButton (same pattern, no error handling)
  • web/src/index.css:298–300 — Streamdown code block styling

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions