| author | |
| committer | |
| log | feb3ea7ebf83de2e301adb1f20300b262773ec54 |
| tree | f5d921eb3b7560792e9fe7722d7458eb3e884766 |
| parent | ea71a19d4181ce13b0e8f374f45ba9237ee4a9ce |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>7 files changed, 23 insertions(+), 2 deletions(-)
apps/web/src/components/DiffPanel.tsx+2| ... | ... | @@ -37,6 +37,7 @@ import { |
| 37 | 37 | resolveDiffThemeName, |
| 38 | 38 | resolveFileDiffPath, |
| 39 | 39 | } from "../lib/diffRendering"; |
| 40 | import { PREFERRED_HIGHLIGHTER } from "../lib/syntaxHighlighting"; | |
| 40 | 41 | import { areAllDiffFilesCollapsed, toggleAllDiffFiles } from "../lib/diffCollapse"; |
| 41 | 42 | import { useTurnDiffSummaries } from "../hooks/useTurnDiffSummaries"; |
| 42 | 43 | import { useWorkspaceMutationRefresh } from "../hooks/useWorkspaceMutationRefresh"; |
| ... | ... | @@ -948,6 +949,7 @@ export default function DiffPanel({ |
| 948 | 949 | lineDiffType: "none", |
| 949 | 950 | overflow: wordWrap ? "wrap" : "scroll", |
| 950 | 951 | theme: resolveDiffThemeName(resolvedTheme), |
| 952 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 951 | 953 | themeType: resolvedTheme as DiffThemeType, |
| 952 | 954 | stickyHeaders: true, |
| 953 | 955 | ...(loadDiffFiles ? { loadDiffFiles } : {}), |
apps/web/src/components/DiffWorkerPoolProvider.tsx+2| ... | ... | @@ -4,6 +4,7 @@ import * as Schema from "effect/Schema"; |
| 4 | 4 | import { useEffect, useMemo, type ReactNode } from "react"; |
| 5 | 5 | import { useTheme } from "../hooks/useTheme"; |
| 6 | 6 | import { resolveDiffThemeName, type DiffThemeName } from "../lib/diffRendering"; |
| 7 | import { PREFERRED_HIGHLIGHTER } from "../lib/syntaxHighlighting"; | |
| 7 | 8 | |
| 8 | 9 | export class DiffWorkerError extends Schema.TaggedErrorClass<DiffWorkerError>()("DiffWorkerError", { |
| 9 | 10 | operation: Schema.Literals(["create-worker", "get-render-options", "set-render-options"]), |
| ... | ... | @@ -73,6 +74,7 @@ export function DiffWorkerPoolProvider({ children }: { children?: ReactNode }) { |
| 73 | 74 | }} |
| 74 | 75 | highlighterOptions={{ |
| 75 | 76 | theme: diffThemeName, |
| 77 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 76 | 78 | tokenizeMaxLineLength: 1_000, |
| 77 | 79 | useTokenTransformer: true, |
| 78 | 80 | }} |
apps/web/src/components/chat/MessagesTimeline.tsx+2| ... | ... | @@ -59,6 +59,7 @@ import { |
| 59 | 59 | resolveDiffThemeName, |
| 60 | 60 | resolveFileDiffPath, |
| 61 | 61 | } from "../../lib/diffRendering"; |
| 62 | import { PREFERRED_HIGHLIGHTER } from "../../lib/syntaxHighlighting"; | |
| 62 | 63 | import ChatMarkdown, { ChatMarkdownAssetImage } from "../ChatMarkdown"; |
| 63 | 64 | import { |
| 64 | 65 | BotIcon, |
| ... | ... | @@ -2060,6 +2061,7 @@ function UserMessageReviewCommentCard({ comment }: { comment: ReviewCommentConte |
| 2060 | 2061 | collapsed: false, |
| 2061 | 2062 | diffStyle: "unified", |
| 2062 | 2063 | theme: resolveDiffThemeName(ctx.resolvedTheme), |
| 2064 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 2063 | 2065 | }} |
| 2064 | 2066 | /> |
| 2065 | 2067 | ))} |
apps/web/src/components/files/FilePreviewPanel.tsx+3| ... | ... | @@ -31,6 +31,7 @@ import { useTheme } from "~/hooks/useTheme"; |
| 31 | 31 | import { getLocalStorageItem, setLocalStorageItem, useLocalStorage } from "~/hooks/useLocalStorage"; |
| 32 | 32 | import { useWorkspaceMutationRefresh } from "~/hooks/useWorkspaceMutationRefresh"; |
| 33 | 33 | import { DIFF_SURFACE_THEME_UNSAFE_CSS, resolveDiffThemeName } from "~/lib/diffRendering"; |
| 34 | import { PREFERRED_HIGHLIGHTER } from "~/lib/syntaxHighlighting"; | |
| 34 | 35 | import { cn } from "~/lib/utils"; |
| 35 | 36 | import { isPreviewSupportedInRuntime } from "~/previewStateStore"; |
| 36 | 37 | import { resolvePathLinkTarget } from "~/terminal-links"; |
| ... | ... | @@ -755,6 +756,7 @@ function EditableFileSurface({ |
| 755 | 756 | onLineSelectionEnd: handleLineSelectionEnd, |
| 756 | 757 | overflow: wordWrap ? "wrap" : "scroll", |
| 757 | 758 | theme: resolveDiffThemeName(resolvedTheme), |
| 759 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 758 | 760 | themeType: resolvedTheme, |
| 759 | 761 | unsafeCSS: FILE_LINK_REVEAL_UNSAFE_CSS, |
| 760 | 762 | onPostRender: handlePostRender, |
| ... | ... | @@ -1149,6 +1151,7 @@ export default function FilePreviewPanel({ |
| 1149 | 1151 | disableFileHeader: true, |
| 1150 | 1152 | overflow: wordWrap ? "wrap" : "scroll", |
| 1151 | 1153 | theme: resolveDiffThemeName(resolvedTheme), |
| 1154 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 1152 | 1155 | themeType: resolvedTheme, |
| 1153 | 1156 | unsafeCSS: FILE_LINK_REVEAL_UNSAFE_CSS, |
| 1154 | 1157 | onPostRender: onFilePostRender, |
apps/web/src/components/pullRequest/PullRequestCodeTab.tsx+2| ... | ... | @@ -42,6 +42,7 @@ import { |
| 42 | 42 | resolveFileDiffPreviousPath, |
| 43 | 43 | type RenderablePatch, |
| 44 | 44 | } from "~/lib/diffRendering"; |
| 45 | import { PREFERRED_HIGHLIGHTER } from "~/lib/syntaxHighlighting"; | |
| 45 | 46 | import { cn } from "~/lib/utils"; |
| 46 | 47 | import { createPullRequestDiffFileContentsLoader } from "~/lib/diffFileContents"; |
| 47 | 48 | import { |
| ... | ... | @@ -743,6 +744,7 @@ export function PullRequestCodeTab({ |
| 743 | 744 | lineDiffType: "none" as const, |
| 744 | 745 | overflow: wordWrap ? ("wrap" as const) : ("scroll" as const), |
| 745 | 746 | theme: resolveDiffThemeName(resolvedTheme), |
| 747 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 746 | 748 | themeType: resolvedTheme, |
| 747 | 749 | stickyHeaders: true, |
| 748 | 750 | loadDiffFiles, |
apps/web/src/components/settings/SettingsFontPreviews.tsx+2-1| ... | ... | @@ -5,6 +5,7 @@ import { terminalThemeFromApp } from "../ThreadTerminalDrawer"; |
| 5 | 5 | import { useTheme } from "../../hooks/useTheme"; |
| 6 | 6 | import { DISCONNECTED_COMPOSER_PLACEHOLDER } from "../../composerPlaceholder"; |
| 7 | 7 | import { resolveDiffThemeName, type DiffThemeName } from "../../lib/diffRendering"; |
| 8 | import { PREFERRED_HIGHLIGHTER } from "../../lib/syntaxHighlighting"; | |
| 8 | 9 | import { GhosttyTerminalSurface } from "~/terminal/ghostty/surface"; |
| 9 | 10 | |
| 10 | 11 | // The font previews are the real surfaces, not lookalikes: the composer's |
| ... | ... | @@ -79,7 +80,7 @@ function loadDiffPreviewHtml(theme: DiffThemeName): Promise<readonly string[]> { |
| 79 | 80 | if (promise === undefined) { |
| 80 | 81 | promise = preloadPatchFile({ |
| 81 | 82 | patch: DIFF_PREVIEW_PATCH, |
| 82 | options: { diffStyle: "unified", theme }, | |
| 83 | options: { diffStyle: "unified", theme, preferredHighlighter: PREFERRED_HIGHLIGHTER }, | |
| 83 | 84 | }).then((results) => results.map((result) => result.prerenderedHTML)); |
| 84 | 85 | diffPreviewHtmlByTheme.set(theme, promise); |
| 85 | 86 | } |
apps/web/src/lib/syntaxHighlighting.ts+10-1| ... | ... | @@ -1,11 +1,20 @@ |
| 1 | 1 | import { |
| 2 | 2 | getSharedHighlighter, |
| 3 | 3 | type DiffsHighlighter, |
| 4 | type HighlighterTypes, | |
| 4 | 5 | type SupportedLanguages, |
| 5 | 6 | } from "@pierre/diffs"; |
| 6 | 7 | |
| 7 | 8 | import { resolveDiffThemeName } from "./diffRendering"; |
| 8 | 9 | |
| 10 | /** | |
| 11 | * Always highlight with the Oniguruma WASM engine — the JS regex engine can | |
| 12 | * backtrack catastrophically and hang the tokenizing thread. The shared | |
| 13 | * highlighter is a first-caller-wins singleton, so every creation site must | |
| 14 | * pass this value. | |
| 15 | */ | |
| 16 | export const PREFERRED_HIGHLIGHTER: HighlighterTypes = "shiki-wasm"; | |
| 17 | ||
| 9 | 18 | const highlighterPromiseCache = new Map<string, Promise<DiffsHighlighter>>(); |
| 10 | 19 | |
| 11 | 20 | export function getSyntaxHighlighterPromise(language: string): Promise<DiffsHighlighter> { |
| ... | ... | @@ -15,7 +24,7 @@ export function getSyntaxHighlighterPromise(language: string): Promise<DiffsHigh |
| 15 | 24 | const promise = getSharedHighlighter({ |
| 16 | 25 | themes: [resolveDiffThemeName("dark"), resolveDiffThemeName("light")], |
| 17 | 26 | langs: [language as SupportedLanguages], |
| 18 | preferredHighlighter: "shiki-js", | |
| 27 | preferredHighlighter: PREFERRED_HIGHLIGHTER, | |
| 19 | 28 | }).catch((error) => { |
| 20 | 29 | if (language === "text") { |
| 21 | 30 | highlighterPromiseCache.delete(language); |