diff --git a/resources/js/components/code-block.tsx b/resources/js/components/code-block.tsx index cf04563d3..96e2b0486 100644 --- a/resources/js/components/code-block.tsx +++ b/resources/js/components/code-block.tsx @@ -7,10 +7,12 @@ import { cn } from '@/lib/utils'; export function CodeBlock({ html, + fullHtml, language, isCopyable = true, }: { html: string; + fullHtml?: string; language: string | null; isCopyable?: boolean; }) { @@ -34,7 +36,16 @@ export function CodeBlock({ return; } - if (await copy(codeRef.current.textContent ?? '')) { + let code = codeRef.current.textContent ?? ''; + + if (fullHtml !== undefined) { + // The server escapes the code while highlighting it, and a template runs nothing. + const template = document.createElement('template'); + template.innerHTML = fullHtml; + code = template.content.textContent ?? ''; + } + + if (await copy(code)) { setIsCopied(true); return; diff --git a/resources/js/components/commit-body.tsx b/resources/js/components/commit-body.tsx index fac23d0a0..6ecabe883 100644 --- a/resources/js/components/commit-body.tsx +++ b/resources/js/components/commit-body.tsx @@ -91,6 +91,7 @@ export function BodySegments({ @@ -147,7 +148,11 @@ export function FoldedCommitBody({ return (
@@ -165,6 +170,24 @@ export function FoldedCommitBody({ ); } +/** + * The preview is the start of the body, so its code blocks are the body's first ones, in order. The last may be cut + * to its first lines; each gets the whole block to copy. + */ +function withFullBlocks( + previewSegments: BodySegment[], + segments: BodySegment[], +): BodySegment[] { + const blocks = segments.filter((segment) => segment.type === 'block'); + let index = 0; + + return previewSegments.map((segment) => + segment.type === 'block' + ? { ...segment, fullHtml: blocks[index++]?.html } + : segment, + ); +} + export function CommitBodyExcerpt({ segments, previewSegments = null, diff --git a/resources/js/types/commit.ts b/resources/js/types/commit.ts index 12790e4d3..4e6b99583 100644 --- a/resources/js/types/commit.ts +++ b/resources/js/types/commit.ts @@ -50,7 +50,13 @@ export type CommitImage = { export type BodySegment = | { type: 'text'; text: string } | { type: 'code'; code: string } - | { type: 'block'; language: string | null; html: string }; + | { + type: 'block'; + language: string | null; + html: string; + // The whole block, when a preview shows only its first lines, so copying it still copies all of it. + fullHtml?: string; + }; export type CommitTranslation = { language: string;