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