feat(messages): code blocks get a Wrap toggle next to Copy (#131)

Verified upstream first: the Copy chip (with a 'Copied' state) already exists
on every code block. Added the missing half: a Wrap chip that switches long
lines to pre-wrap (default stays no-wrap + horizontal scroll); the choice is
remembered for the session, not persisted. Markdown/HTML output, layout widths
and the syntax theme are untouched. Verified headless: scrollWidth 1016 →
612 px with wrapping on, 'Copied' state on copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
2026-09-19 23:09:25 -04:00
co-authored by Claude Opus 5
parent 9363629ea2
commit 2a0c409180
+26 -8
View File
@@ -333,13 +333,10 @@ const extractTextFromChildren = (nodes: ChildNode[]): string => {
return text;
};
export function CodeBlock({
children,
opts,
}: {
children: ChildNode[];
opts: HTMLReactParserOptions;
}) {
export // Session memory for the code-block Wrap toggle (#131).
const codeWrapPreference = { value: false };
function CodeBlock({ children, opts }: { children: ChildNode[]; opts: HTMLReactParserOptions }) {
const code = children[0];
const attribs = code instanceof Element && code.name === 'code' ? code.attribs : undefined;
const languageClass = attribs?.class;
@@ -357,6 +354,13 @@ export function CodeBlock({
const [expanded, setExpand] = useState(false);
const [copied, setCopied] = useTimeoutToggle();
// [Gitea #131] Line-wrap toggle for long lines; the choice is remembered for
// the session (module-level), not persisted.
const [wrap, setWrap] = useState(codeWrapPreference.value);
const toggleWrap = () => {
codeWrapPreference.value = !wrap;
setWrap(!wrap);
};
const handleCopy = () => {
copyToClipboard(extractTextFromChildren(children));
@@ -376,6 +380,16 @@ export function CodeBlock({
</Text>
</Box>
<Box shrink="No" gap="200">
<Chip
variant={wrap ? 'Primary' : 'Surface'}
fill="None"
radii="Pill"
onClick={toggleWrap}
aria-pressed={wrap}
title={wrap ? 'Scroll long lines instead of wrapping' : 'Wrap long lines'}
>
<Text size="B300">Wrap</Text>
</Chip>
<Chip
variant={copied ? 'Success' : 'Surface'}
fill="None"
@@ -410,7 +424,11 @@ export function CodeBlock({
visibility="Hover"
hideTrack
>
<div id="code-block-content" className={css.CodeBlockInternal}>
<div
id="code-block-content"
className={css.CodeBlockInternal}
style={wrap ? { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' } : undefined}
>
{domToReact(children as unknown as DOMNode[], opts)}
</div>
</Scroll>