From be42bbbc86071ccd2e945562e1ab4c7a241bdcdc Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 24 Mar 2026 14:12:06 -0700 Subject: [PATCH 1/4] fix(web): fix line numbers selectable in Safari Add WebkitUserSelect: 'none' to prevent line numbers from being selected on Safari, which requires the -webkit-user-select vendor prefix. Co-Authored-By: Claude Sonnet 4.6 --- .../src/app/[domain]/components/lightweightCodeHighlighter.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx b/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx index f72e64854..4ad81ba67 100644 --- a/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx +++ b/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx @@ -145,6 +145,7 @@ export const LightweightCodeHighlighter = memo((prop textAlign: 'left', paddingLeft: '5px', userSelect: 'none', + WebkitUserSelect: 'none', fontFamily: tailwind.theme.fontFamily.editor, color: tailwind.theme.colors.editor.gutterForeground, }} From d65648553b184bbe8c807e521cbe768c4ca3a79a Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 24 Mar 2026 14:12:51 -0700 Subject: [PATCH 2/4] chore: update CHANGELOG for #1037 Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 422e90749..cb49ad4d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Fixed line numbers being selectable in Safari in the lightweight code highlighter. [#1037](https://github.com/sourcebot-dev/sourcebot/pull/1037) + ## [4.16.1] - 2026-03-24 ### Fixed From d5a111c58cac141a293ce609bceab5646da52433 Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 24 Mar 2026 14:21:44 -0700 Subject: [PATCH 3/4] feat(web): add copy button to lightweight code highlighter Adds an optional isCopyButtonVisible prop (default false) that renders a copy-to-clipboard button in the top-right corner, visible on hover. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 3 + .../components/lightweightCodeHighlighter.tsx | 96 +++++++++++-------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb49ad4d6..12f4b92ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed line numbers being selectable in Safari in the lightweight code highlighter. [#1037](https://github.com/sourcebot-dev/sourcebot/pull/1037) +### Added +- Added optional copy button to the lightweight code highlighter (`isCopyButtonVisible` prop), shown on hover. [#1037](https://github.com/sourcebot-dev/sourcebot/pull/1037) + ## [4.16.1] - 2026-03-24 ### Fixed diff --git a/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx b/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx index 4ad81ba67..f1d922cf9 100644 --- a/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx +++ b/packages/web/src/app/[domain]/components/lightweightCodeHighlighter.tsx @@ -2,11 +2,12 @@ import { Parser } from '@lezer/common' import { LanguageDescription, StreamLanguage } from '@codemirror/language' import { Highlighter, highlightTree } from '@lezer/highlight' import { languages as builtinLanguages } from '@codemirror/language-data' -import { memo, useEffect, useMemo, useState } from 'react' +import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { useCodeMirrorHighlighter } from '@/hooks/useCodeMirrorHighlighter' import tailwind from '@/tailwind' import { measure } from '@/lib/utils' import { SourceRange } from '@/features/search' +import { CopyIconButton } from './copyIconButton' // Define a plain text language const plainTextLanguage = StreamLanguage.define({ @@ -25,6 +26,7 @@ interface LightweightCodeHighlighter { /* 1-based line number offset */ lineNumbersOffset?: number; renderWhitespace?: boolean; + isCopyButtonVisible?: boolean; } // The maximum number of characters per line that we will display in the preview. @@ -46,6 +48,7 @@ export const LightweightCodeHighlighter = memo((prop lineNumbers = false, lineNumbersOffset = 1, renderWhitespace = false, + isCopyButtonVisible = false, } = props; const unhighlightedLines = useMemo(() => { @@ -110,6 +113,15 @@ export const LightweightCodeHighlighter = memo((prop isFileTooLargeToDisplay, ]); + const onCopy = useCallback(() => { + try { + navigator.clipboard.writeText(code); + return true; + } catch { + return false; + } + }, [code]); + const lineCount = (highlightedLines ?? unhighlightedLines).length + lineNumbersOffset; const lineNumberDigits = String(lineCount).length; const lineNumberWidth = `${lineNumberDigits + 2}ch`; // +2 for padding @@ -123,47 +135,55 @@ export const LightweightCodeHighlighter = memo((prop } return ( -
- {(highlightedLines ?? unhighlightedLines).map((line, index) => ( -
- {lineNumbers && ( +
+ {isCopyButtonVisible && ( + + )} +
+ {(highlightedLines ?? unhighlightedLines).map((line, index) => ( +
+ {lineNumbers && ( + + {index + lineNumbersOffset} + + )} - {index + lineNumbersOffset} + {line} - )} - - {line} - -
- ))} +
+ ))} +
) }) @@ -185,7 +205,7 @@ async function getCodeParser( return null; } - if (!found.support) { + if (!found.support) { await found.load(); } return found.support ? found.support.language.parser : null; From f7ab0b881c5e1a3dbbd0715eb8ea39a54109c428 Mon Sep 17 00:00:00 2001 From: Brendan Kellam Date: Tue, 24 Mar 2026 14:22:43 -0700 Subject: [PATCH 4/4] s --- .../web/src/features/chat/components/chatThread/codeBlock.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/web/src/features/chat/components/chatThread/codeBlock.tsx b/packages/web/src/features/chat/components/chatThread/codeBlock.tsx index ce9aacd63..45b02c07f 100644 --- a/packages/web/src/features/chat/components/chatThread/codeBlock.tsx +++ b/packages/web/src/features/chat/components/chatThread/codeBlock.tsx @@ -41,6 +41,7 @@ export const CodeBlock = ({ language={language} lineNumbers={true} renderWhitespace={true} + isCopyButtonVisible={true} > {code}