From 6ea4979676cd9f2312afb17acd4508dde54b47da Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sat, 18 Apr 2026 18:20:36 -0400 Subject: [PATCH 1/2] chore: trim Shiki bundles and rename themes to diffkit - Use createHighlighterCore with explicit @shikijs/langs and JS regex engine for markdown highlighting; drop loadLanguage fallback to full catalog. - Patch @pierre/diffs to avoid bundledLanguages/bundledThemes from shiki; align on shiki 4 via pnpm overrides. - Rename custom markdown themes from vercel-* to diffkit-*. --- package.json | 9 +- packages/ui/package.json | 1 + packages/ui/src/components/markdown.tsx | 75 +++++-------- packages/ui/src/lib/diffs-themes.ts | 4 +- packages/ui/src/lib/shiki-bundle.ts | 87 +++++++++++++++ packages/ui/src/lib/shiki-themes.ts | 20 ++-- patches/@pierre__diffs@1.1.12.patch | 141 ++++++++++++++++++++++++ pnpm-lock.yaml | 94 ++++------------ 8 files changed, 299 insertions(+), 132 deletions(-) create mode 100644 packages/ui/src/lib/shiki-bundle.ts create mode 100644 patches/@pierre__diffs@1.1.12.patch diff --git a/package.json b/package.json index 606f70e..f4202a7 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,13 @@ "lightningcss", "sharp", "workerd" - ] + ], + "overrides": { + "@pierre/diffs>shiki": "4.0.2", + "@pierre/diffs>@shikijs/transformers": "4.0.2" + }, + "patchedDependencies": { + "@pierre/diffs@1.1.12": "patches/@pierre__diffs@1.1.12.patch" + } } } diff --git a/packages/ui/package.json b/packages/ui/package.json index e31b0be..126aebd 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -37,6 +37,7 @@ "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@shikijs/langs": "4.0.2", "@shikijs/rehype": "^4.0.2", "@tailwindcss/typography": "^0.5.19", "class-variance-authority": "^0.7.1", diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index ca60567..816b8ba 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -11,49 +11,34 @@ import { import rehypeRaw from "rehype-raw"; import remarkGfm from "remark-gfm"; import { remarkAlert } from "remark-github-blockquote-alert"; -import type { BundledLanguage, Highlighter } from "shiki"; -import { vercelDark, vercelLight } from "../lib/shiki-themes"; +import { + createMarkdownHighlighter, + type MarkdownHighlighter, + type ShikiBundledLang, + shikiBundledLangSet, +} from "../lib/shiki-bundle"; import { cn } from "../lib/utils"; -const PRELOADED_LANGS: BundledLanguage[] = [ - "javascript", - "typescript", - "jsx", - "tsx", - "json", - "html", - "css", - "bash", - "shell", - "python", - "go", - "rust", - "yaml", - "markdown", - "diff", - "sql", - "graphql", - "ruby", - "java", - "c", - "cpp", - "swift", - "kotlin", - "dockerfile", - "toml", -]; +const FENCE_LANG_ALIASES: Record = { + shell: "shellscript", + sh: "shellscript", + zsh: "shellscript", + console: "shellscript", + js: "javascript", + ts: "typescript", + py: "python", + rs: "rust", + rb: "ruby", + kt: "kotlin", + cs: "csharp", +}; // Eagerly start loading the highlighter at module level (client-only to avoid // bundling all shiki language grammars into the server bundle for CF Workers). -const highlighterPromise: Promise = +const highlighterPromise: Promise = typeof window !== "undefined" - ? import("shiki").then((shiki) => - shiki.createHighlighter({ - themes: [vercelLight, vercelDark], - langs: PRELOADED_LANGS, - }), - ) - : new Promise(() => {}); // Never resolves on server → Suspense fallback + ? createMarkdownHighlighter() + : new Promise(() => {}); // Never resolves on server → Suspense fallback const htmlCache = new Map>(); @@ -96,18 +81,14 @@ export function highlightCode(code: string, lang: string): Promise { const cached = htmlCache.get(key); if (cached) return cached; - const promise = highlighterPromise.then(async (highlighter) => { - let effectiveLang = lang; - if (!highlighter.getLoadedLanguages().includes(lang)) { - try { - await highlighter.loadLanguage(lang as BundledLanguage); - } catch { - effectiveLang = "text"; - } - } + const promise = highlighterPromise.then((highlighter) => { + const normalized = + FENCE_LANG_ALIASES[lang] ?? + (shikiBundledLangSet.has(lang) ? (lang as ShikiBundledLang) : null); + const effectiveLang = normalized ?? "text"; return highlighter.codeToHtml(code, { lang: effectiveLang, - themes: { light: "vercel-light", dark: "vercel-dark" }, + themes: { light: "diffkit-light", dark: "diffkit-dark" }, defaultColor: false, }); }); diff --git a/packages/ui/src/lib/diffs-themes.ts b/packages/ui/src/lib/diffs-themes.ts index 9ea0246..179dc1a 100644 --- a/packages/ui/src/lib/diffs-themes.ts +++ b/packages/ui/src/lib/diffs-themes.ts @@ -8,8 +8,8 @@ import type { ThemeRegistrationRaw } from "shiki"; // addition/deletion colors, etc.). // // The `colors` object drives the diff chrome; `tokenColors` drives syntax -// highlighting. We reuse the same Vercel-inspired token palette from -// shiki-themes.ts but pair it with our own editor/UI chrome colors so the +// highlighting. We reuse the same token palette as shiki-themes.ts (diffkit +// light/dark) but pair it with our own editor/UI chrome colors so the // diff viewer feels native to the app. // --------------------------------------------------------------------------- diff --git a/packages/ui/src/lib/shiki-bundle.ts b/packages/ui/src/lib/shiki-bundle.ts new file mode 100644 index 0000000..03ba028 --- /dev/null +++ b/packages/ui/src/lib/shiki-bundle.ts @@ -0,0 +1,87 @@ +import { createHighlighterCore } from "shiki/core"; +import { createJavaScriptRegexEngine } from "shiki/engine/javascript"; +import { diffkitDark, diffkitLight } from "./shiki-themes"; + +/** + * Fine-grained Shiki bundle: only these grammars are ever loaded (see shiki.style/guide/bundles). + * Unknown fence languages fall back to unstyled output via `text` in highlight callers. + */ +export const SHIKI_BUNDLED_LANGS = [ + "javascript", + "typescript", + "jsx", + "tsx", + "json", + "html", + "css", + "bash", + "shellscript", + "python", + "go", + "rust", + "yaml", + "markdown", + "diff", + "sql", + "graphql", + "ruby", + "java", + "c", + "cpp", + "swift", + "kotlin", + "dockerfile", + "toml", + "vue", + "svelte", + "php", + "csharp", +] as const; + +export type ShikiBundledLang = (typeof SHIKI_BUNDLED_LANGS)[number]; + +const LANG_IMPORTS = { + javascript: () => import("@shikijs/langs/javascript"), + typescript: () => import("@shikijs/langs/typescript"), + jsx: () => import("@shikijs/langs/jsx"), + tsx: () => import("@shikijs/langs/tsx"), + json: () => import("@shikijs/langs/json"), + html: () => import("@shikijs/langs/html"), + css: () => import("@shikijs/langs/css"), + bash: () => import("@shikijs/langs/bash"), + shellscript: () => import("@shikijs/langs/shellscript"), + python: () => import("@shikijs/langs/python"), + go: () => import("@shikijs/langs/go"), + rust: () => import("@shikijs/langs/rust"), + yaml: () => import("@shikijs/langs/yaml"), + markdown: () => import("@shikijs/langs/markdown"), + diff: () => import("@shikijs/langs/diff"), + sql: () => import("@shikijs/langs/sql"), + graphql: () => import("@shikijs/langs/graphql"), + ruby: () => import("@shikijs/langs/ruby"), + java: () => import("@shikijs/langs/java"), + c: () => import("@shikijs/langs/c"), + cpp: () => import("@shikijs/langs/cpp"), + swift: () => import("@shikijs/langs/swift"), + kotlin: () => import("@shikijs/langs/kotlin"), + dockerfile: () => import("@shikijs/langs/dockerfile"), + toml: () => import("@shikijs/langs/toml"), + vue: () => import("@shikijs/langs/vue"), + svelte: () => import("@shikijs/langs/svelte"), + php: () => import("@shikijs/langs/php"), + csharp: () => import("@shikijs/langs/csharp"), +} as const satisfies Record Promise>; + +export const shikiBundledLangSet = new Set(SHIKI_BUNDLED_LANGS); + +export type MarkdownHighlighter = Awaited< + ReturnType +>; + +export function createMarkdownHighlighter(): Promise { + return createHighlighterCore({ + themes: [diffkitLight, diffkitDark], + langs: SHIKI_BUNDLED_LANGS.map((id) => LANG_IMPORTS[id]), + engine: createJavaScriptRegexEngine(), + }); +} diff --git a/packages/ui/src/lib/shiki-themes.ts b/packages/ui/src/lib/shiki-themes.ts index 2b1f5c4..764c6bf 100644 --- a/packages/ui/src/lib/shiki-themes.ts +++ b/packages/ui/src/lib/shiki-themes.ts @@ -1,6 +1,6 @@ import type { ThemeRegistrationRaw } from "shiki"; -const vercelLightTokens: ThemeRegistrationRaw["tokenColors"] = [ +const diffkitLightTokens: ThemeRegistrationRaw["tokenColors"] = [ { scope: ["comment", "punctuation.definition.comment"], settings: { foreground: "#666666", fontStyle: "italic" }, @@ -132,7 +132,7 @@ const vercelLightTokens: ThemeRegistrationRaw["tokenColors"] = [ }, ]; -const vercelDarkTokens: ThemeRegistrationRaw["tokenColors"] = [ +const diffkitDarkTokens: ThemeRegistrationRaw["tokenColors"] = [ { scope: ["comment", "punctuation.definition.comment"], settings: { foreground: "#a1a1a1", fontStyle: "italic" }, @@ -264,24 +264,24 @@ const vercelDarkTokens: ThemeRegistrationRaw["tokenColors"] = [ }, ]; -export const vercelLight: ThemeRegistrationRaw = { - name: "vercel-light", +export const diffkitLight: ThemeRegistrationRaw = { + name: "diffkit-light", type: "light", - settings: vercelLightTokens as ThemeRegistrationRaw["settings"], + settings: diffkitLightTokens as ThemeRegistrationRaw["settings"], colors: { "editor.background": "#ffffff", "editor.foreground": "#171717", }, - tokenColors: vercelLightTokens, + tokenColors: diffkitLightTokens, }; -export const vercelDark: ThemeRegistrationRaw = { - name: "vercel-dark", +export const diffkitDark: ThemeRegistrationRaw = { + name: "diffkit-dark", type: "dark", - settings: vercelDarkTokens as ThemeRegistrationRaw["settings"], + settings: diffkitDarkTokens as ThemeRegistrationRaw["settings"], colors: { "editor.background": "#1a1a1a", "editor.foreground": "#ededed", }, - tokenColors: vercelDarkTokens, + tokenColors: diffkitDarkTokens, }; diff --git a/patches/@pierre__diffs@1.1.12.patch b/patches/@pierre__diffs@1.1.12.patch new file mode 100644 index 0000000..e7596be --- /dev/null +++ b/patches/@pierre__diffs@1.1.12.patch @@ -0,0 +1,141 @@ +diff --git a/dist/highlighter/languages/resolveLanguage.js b/dist/highlighter/languages/resolveLanguage.js +index f5dff39f418566d009c46b31601d39f7a1e9c19b..eed7279e038551ec2cfd600afbd170510991b243 100644 +--- a/dist/highlighter/languages/resolveLanguage.js ++++ b/dist/highlighter/languages/resolveLanguage.js +@@ -1,16 +1,52 @@ +-import { RegisteredCustomLanguages, ResolvedLanguages, ResolvingLanguages } from "./constants.js"; + import { isWorkerContext } from "../../utils/isWorkerContext.js"; +-import { bundledLanguages } from "shiki"; ++import { RegisteredCustomLanguages, ResolvedLanguages, ResolvingLanguages } from "./constants.js"; + + //#region src/highlighter/languages/resolveLanguage.ts ++/** Fine-grained language loaders only — avoids `import { bundledLanguages } from "shiki"` (pulls every grammar chunk). */ ++const trimmedBundledLanguages = { ++ javascript: () => import("@shikijs/langs/javascript"), ++ typescript: () => import("@shikijs/langs/typescript"), ++ jsx: () => import("@shikijs/langs/jsx"), ++ tsx: () => import("@shikijs/langs/tsx"), ++ json: () => import("@shikijs/langs/json"), ++ html: () => import("@shikijs/langs/html"), ++ css: () => import("@shikijs/langs/css"), ++ bash: () => import("@shikijs/langs/bash"), ++ shellscript: () => import("@shikijs/langs/shellscript"), ++ zsh: () => import("@shikijs/langs/shellscript"), ++ python: () => import("@shikijs/langs/python"), ++ go: () => import("@shikijs/langs/go"), ++ rust: () => import("@shikijs/langs/rust"), ++ yaml: () => import("@shikijs/langs/yaml"), ++ markdown: () => import("@shikijs/langs/markdown"), ++ diff: () => import("@shikijs/langs/diff"), ++ sql: () => import("@shikijs/langs/sql"), ++ graphql: () => import("@shikijs/langs/graphql"), ++ ruby: () => import("@shikijs/langs/ruby"), ++ java: () => import("@shikijs/langs/java"), ++ c: () => import("@shikijs/langs/c"), ++ cpp: () => import("@shikijs/langs/cpp"), ++ swift: () => import("@shikijs/langs/swift"), ++ kotlin: () => import("@shikijs/langs/kotlin"), ++ dockerfile: () => import("@shikijs/langs/dockerfile"), ++ toml: () => import("@shikijs/langs/toml"), ++ vue: () => import("@shikijs/langs/vue"), ++ svelte: () => import("@shikijs/langs/svelte"), ++ php: () => import("@shikijs/langs/php"), ++ csharp: () => import("@shikijs/langs/csharp"), ++ ini: () => import("@shikijs/langs/ini"), ++}; ++ ++const FALLBACK_LANG = "ini"; ++ + async function resolveLanguage(lang) { + if (isWorkerContext()) throw new Error(`resolveLanguage("${lang}") cannot be called from a worker context. Languages must be pre-resolved on the main thread and passed to the worker via the resolvedLanguages parameter.`); + const resolver = ResolvingLanguages.get(lang); + if (resolver != null) return resolver; + try { + let loader = RegisteredCustomLanguages.get(lang); +- if (loader == null && Object.prototype.hasOwnProperty.call(bundledLanguages, lang)) loader = bundledLanguages[lang]; +- if (loader == null) throw new Error(`resolveLanguage: "${lang}" not found in bundled or custom languages`); ++ if (loader == null && Object.prototype.hasOwnProperty.call(trimmedBundledLanguages, lang)) loader = trimmedBundledLanguages[lang]; ++ if (loader == null) loader = trimmedBundledLanguages[FALLBACK_LANG]; + const resolver$1 = loader().then(({ default: data }) => { + const resolvedLang = { + name: lang, +diff --git a/dist/highlighter/shared_highlighter.js b/dist/highlighter/shared_highlighter.js +index ae0e90172df19fddd8ae8bcb7feef9fb92d5c3a6..c817426868f0d6b52cf62ad58b06665779be0a04 100644 +--- a/dist/highlighter/shared_highlighter.js ++++ b/dist/highlighter/shared_highlighter.js +@@ -5,14 +5,16 @@ import { attachResolvedThemes } from "./themes/attachResolvedThemes.js"; + import { cleanUpResolvedThemes } from "./themes/cleanUpResolvedThemes.js"; + import { getResolvedOrResolveTheme } from "./themes/getResolvedOrResolveTheme.js"; + import { registerCustomTheme } from "./themes/registerCustomTheme.js"; +-import { createHighlighter, createJavaScriptRegexEngine, createOnigurumaEngine } from "shiki"; ++import { createHighlighterCore } from "shiki/core"; ++import { createJavaScriptRegexEngine } from "shiki/engine/javascript"; ++import { createOnigurumaEngine } from "shiki/engine/oniguruma"; + + //#region src/highlighter/shared_highlighter.ts + let highlighter; + async function getSharedHighlighter({ themes, langs, preferredHighlighter = "shiki-js" }) { +- highlighter ??= createHighlighter({ ++ highlighter ??= createHighlighterCore({ + themes: [], +- langs: ["text"], ++ langs: [], + engine: preferredHighlighter === "shiki-wasm" ? createOnigurumaEngine(import("shiki/wasm")) : createJavaScriptRegexEngine() + }); + const instance = isHighlighterLoading(highlighter) ? await highlighter : highlighter; +diff --git a/dist/highlighter/themes/registerCustomCSSVariableTheme.js b/dist/highlighter/themes/registerCustomCSSVariableTheme.js +index d5991ef17e121ea578a00badb4bd10cfdc34bde8..656f0ff670e45b840c5b5a6bb0488db7c4377bac 100644 +--- a/dist/highlighter/themes/registerCustomCSSVariableTheme.js ++++ b/dist/highlighter/themes/registerCustomCSSVariableTheme.js +@@ -1,6 +1,6 @@ + import { registerCustomTheme } from "./registerCustomTheme.js"; + import { formatCSSVariablePrefix } from "../../utils/formatCSSVariablePrefix.js"; +-import { createCssVariablesTheme } from "shiki"; ++import { createCssVariablesTheme } from "@shikijs/core"; + + //#region src/highlighter/themes/registerCustomCSSVariableTheme.ts + function registerCustomCSSVariableTheme(name, variableDefaults, fontStyle = false) { +diff --git a/dist/highlighter/themes/resolveTheme.js b/dist/highlighter/themes/resolveTheme.js +index 47d44c5dfc723de564b26ad55c6faf9761c81d4f..459225b88d0af753a53b092dea3e60973be5630c 100644 +--- a/dist/highlighter/themes/resolveTheme.js ++++ b/dist/highlighter/themes/resolveTheme.js +@@ -1,6 +1,6 @@ + import { isWorkerContext } from "../../utils/isWorkerContext.js"; + import { RegisteredCustomThemes, ResolvedThemes, ResolvingThemes } from "./constants.js"; +-import { bundledThemes, normalizeTheme } from "shiki"; ++import { normalizeTheme } from "@shikijs/core"; + + //#region src/highlighter/themes/resolveTheme.ts + async function resolveTheme(themeName) { +@@ -8,7 +8,7 @@ async function resolveTheme(themeName) { + const resolver = ResolvingThemes.get(themeName); + if (resolver != null) return resolver; + try { +- const loader = RegisteredCustomThemes.get(themeName) ?? bundledThemes[themeName]; ++ const loader = RegisteredCustomThemes.get(themeName); + if (loader == null) throw new Error(`resolveTheme: No valid loader for ${themeName}`); + const resolver$1 = loader().then((result) => { + return normalizeAndCacheResolvedTheme(themeName, "default" in result ? result.default : result); +diff --git a/dist/index.js b/dist/index.js +index d7cd0c20598c4ed718439601c022ebd997d844ae..9d39686e46ed636c99dc3698a685ef742306e737 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -97,6 +97,6 @@ import { getLineEndingType } from "./utils/getLineEndingType.js"; + import { getSingularPatch } from "./utils/getSingularPatch.js"; + import { setLanguageOverride } from "./utils/setLanguageOverride.js"; + import { trimPatchContext } from "./utils/trimPatchContext.js"; +-import { codeToHtml, createCssVariablesTheme as createCSSVariablesTheme } from "shiki"; ++import { codeToHtml, createCssVariablesTheme as createCSSVariablesTheme } from "@shikijs/core"; + + export { ALTERNATE_FILE_NAMES_GIT, AttachedLanguages, AttachedThemes, COMMIT_METADATA_SPLIT, CORE_CSS_ATTRIBUTE, CUSTOM_EXTENSION_TO_FILE_FORMAT, CUSTOM_HEADER_SLOT_ID, CodeToTokenTransformStream, DEFAULT_COLLAPSED_CONTEXT_THRESHOLD, DEFAULT_EXPANDED_REGION, DEFAULT_RENDER_RANGE, DEFAULT_THEMES, DEFAULT_VIRTUAL_FILE_METRICS, DIFFS_TAG_NAME, DiffHunksRenderer, EMPTY_RENDER_RANGE, EXTENSION_TO_FILE_FORMAT, FILENAME_HEADER_REGEX, FILENAME_HEADER_REGEX_GIT, FILE_CONTEXT_BLOB, File, FileDiff, FileRenderer, FileStream, GIT_DIFF_FILE_BREAK_REGEX, HEADER_METADATA_SLOT_ID, HEADER_PREFIX_SLOT_ID, HUNK_HEADER, INDEX_LINE_METADATA, InteractionManager, MERGE_CONFLICT_BASE_MARKER_REGEX, MERGE_CONFLICT_END_MARKER_REGEX, MERGE_CONFLICT_SEPARATOR_MARKER_REGEX, MERGE_CONFLICT_START_MARKER_REGEX, RegisteredCustomLanguages, RegisteredCustomThemes, ResizeManager, ResolvedLanguages, ResolvedThemes, ResolvingLanguages, ResolvingThemes, SPLIT_WITH_NEWLINES, SVGSpriteSheet, ScrollSyncManager, ShikiStreamTokenizer, THEME_CSS_ATTRIBUTE, UNIFIED_DIFF_FILE_BREAK_REGEX, UNSAFE_CSS_ATTRIBUTE, UnresolvedFile, VirtualizedFile, VirtualizedFileDiff, Virtualizer, areDiffLineAnnotationsEqual, areDiffRenderOptionsEqual, areFilesEqual, areHunkDataEqual, areLanguagesAttached, areLineAnnotationsEqual, areObjectsEqual, areOptionsEqual, arePrePropertiesEqual, areRenderRangesEqual, areSelectionsEqual, areThemesAttached, areThemesEqual, areVirtualWindowSpecsEqual, areWorkerStatsEqual, attachResolvedLanguages, attachResolvedThemes, cleanLastNewline, cleanUpResolvedLanguages, cleanUpResolvedThemes, codeToHtml, createAnnotationElement, createAnnotationWrapperNode, createCSSVariablesTheme, createDiffSpanDecoration, createEmptyRowBuffer, createFileHeaderElement, createGutterGap, createGutterItem, createGutterUtilityContentNode, createGutterUtilityElement, createGutterWrapper, createHastElement, createIconElement, createNoNewlineElement, createPreElement, createPreWrapperProperties, createRowNodes, createSeparator, createSpanFromToken, createStyleElement, createTextNodeElement, createThemeStyleElement, createTransformerWithState, createUnsafeCSSStyleNode, createWindowFromScrollPosition, diffAcceptRejectHunk, disposeHighlighter, extendFileFormatMap, findCodeElement, formatCSSVariablePrefix, getFiletypeFromFileName, getHighlighterIfLoaded, getHighlighterOptions, getHighlighterThemeStyles, getHunkSeparatorSlotName, getIconForType, getLineAnnotationName, getLineEndingType, getLineNodes, getOrCreateCodeNode, getResolvedLanguages, getResolvedOrResolveLanguage, getResolvedOrResolveTheme, getResolvedThemes, getSharedHighlighter, getSingularPatch, getThemes, getTotalLineCountFromHunks, getUnresolvedDiffHunksRendererOptions, hasResolvedLanguages, hasResolvedThemes, isDefaultRenderRange, isHighlighterLoaded, isHighlighterLoading, isHighlighterNull, isWorkerContext, parseDiffFromFile, parseLineType, parsePatchFiles, pluckInteractionOptions, preloadHighlighter, prerenderHTMLIfNecessary, processFile, processLine, processPatch, pushOrJoinSpan, queueRender, registerCustomCSSVariableTheme, registerCustomLanguage, registerCustomTheme, renderDiffWithHighlighter, renderFileWithHighlighter, resolveConflict, resolveLanguage, resolveLanguages, resolveRegion, resolveTheme, resolveThemes, setLanguageOverride, setPreNodeProperties, trimPatchContext, wrapCoreCSS, wrapThemeCSS, wrapUnsafeCSS }; +\ No newline at end of file +diff --git a/dist/utils/createSpanNodeFromToken.js b/dist/utils/createSpanNodeFromToken.js +index f7de46b0a1611e6b90d8c647f99867ad3388b6ae..c25ee86344ad875414044a86ee7d9dbb0ba0806c 100644 +--- a/dist/utils/createSpanNodeFromToken.js ++++ b/dist/utils/createSpanNodeFromToken.js +@@ -1,4 +1,4 @@ +-import { getTokenStyleObject, stringifyTokenStyle } from "shiki"; ++import { getTokenStyleObject, stringifyTokenStyle } from "@shikijs/core"; + + //#region src/utils/createSpanNodeFromToken.ts + function createSpanFromToken(token) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e8d4bc9..e78145d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,15 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + '@pierre/diffs>shiki': 4.0.2 + '@pierre/diffs>@shikijs/transformers': 4.0.2 + +patchedDependencies: + '@pierre/diffs@1.1.12': + hash: 550aa11e6d89777306d9713e0fd0e671f149bff7bd2649ddbc1e72247244eb7a + path: patches/@pierre__diffs@1.1.12.patch + importers: .: @@ -43,7 +52,7 @@ importers: version: 0.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@pierre/diffs': specifier: ^1.1.12 - version: 1.1.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.12(patch_hash=550aa11e6d89777306d9713e0fd0e671f149bff7bd2649ddbc1e72247244eb7a)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tailwindcss/vite': specifier: ^4.1.18 version: 4.2.2(vite@7.3.2(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) @@ -249,6 +258,9 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.1.8 version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@shikijs/langs': + specifier: 4.0.2 + version: 4.0.2 '@shikijs/rehype': specifier: ^4.0.2 version: 4.0.2 @@ -2498,30 +2510,18 @@ packages: cpu: [x64] os: [win32] - '@shikijs/core@3.23.0': - resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} - '@shikijs/core@4.0.2': resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==} engines: {node: '>=20'} - '@shikijs/engine-javascript@3.23.0': - resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} - '@shikijs/engine-javascript@4.0.2': resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} engines: {node: '>=20'} - '@shikijs/engine-oniguruma@3.23.0': - resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} - '@shikijs/engine-oniguruma@4.0.2': resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==} engines: {node: '>=20'} - '@shikijs/langs@3.23.0': - resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} - '@shikijs/langs@4.0.2': resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==} engines: {node: '>=20'} @@ -2534,18 +2534,13 @@ packages: resolution: {integrity: sha512-cmPlKLD8JeojasNFoY64162ScpEdEdQUMuVodPCrv1nx1z3bjmGwoKWDruQWa/ejSznImlaeB0Ty6Q3zPaVQAA==} engines: {node: '>=20'} - '@shikijs/themes@3.23.0': - resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - '@shikijs/themes@4.0.2': resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==} engines: {node: '>=20'} - '@shikijs/transformers@3.23.0': - resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==} - - '@shikijs/types@3.23.0': - resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + '@shikijs/transformers@4.0.2': + resolution: {integrity: sha512-1+L0gf9v+SdDXs08vjaLb3mBFa8U7u37cwcBQIv/HCocLwX69Tt6LpUCjtB+UUTvQxI7BnjZKhN/wMjhHBcJGg==} + engines: {node: '>=20'} '@shikijs/types@4.0.2': resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==} @@ -4641,9 +4636,6 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} - shiki@3.23.0: - resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} - shiki@4.0.2: resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} engines: {node: '>=20'} @@ -6138,16 +6130,16 @@ snapshots: '@opentelemetry/semantic-conventions@1.40.0': {} - '@pierre/diffs@1.1.12(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@pierre/diffs@1.1.12(patch_hash=550aa11e6d89777306d9713e0fd0e671f149bff7bd2649ddbc1e72247244eb7a)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@pierre/theme': 0.0.28 - '@shikijs/transformers': 3.23.0 + '@shikijs/transformers': 4.0.2 diff: 8.0.3 hast-util-to-html: 9.0.5 lru_map: 0.4.1 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - shiki: 3.23.0 + shiki: 4.0.2 '@pierre/theme@0.0.28': {} @@ -6943,13 +6935,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true - '@shikijs/core@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - '@shikijs/core@4.0.2': dependencies: '@shikijs/primitive': 4.0.2 @@ -6958,32 +6943,17 @@ snapshots: '@types/hast': 3.0.4 hast-util-to-html: 9.0.5 - '@shikijs/engine-javascript@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.5 - '@shikijs/engine-javascript@4.0.2': dependencies: '@shikijs/types': 4.0.2 '@shikijs/vscode-textmate': 10.0.2 oniguruma-to-es: 4.3.5 - '@shikijs/engine-oniguruma@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/engine-oniguruma@4.0.2': dependencies: '@shikijs/types': 4.0.2 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/langs@4.0.2': dependencies: '@shikijs/types': 4.0.2 @@ -7003,23 +6973,14 @@ snapshots: unified: 11.0.5 unist-util-visit: 5.1.0 - '@shikijs/themes@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/themes@4.0.2': dependencies: '@shikijs/types': 4.0.2 - '@shikijs/transformers@3.23.0': + '@shikijs/transformers@4.0.2': dependencies: - '@shikijs/core': 3.23.0 - '@shikijs/types': 3.23.0 - - '@shikijs/types@3.23.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 + '@shikijs/core': 4.0.2 + '@shikijs/types': 4.0.2 '@shikijs/types@4.0.2': dependencies: @@ -9427,17 +9388,6 @@ snapshots: shell-quote@1.8.3: {} - shiki@3.23.0: - dependencies: - '@shikijs/core': 3.23.0 - '@shikijs/engine-javascript': 3.23.0 - '@shikijs/engine-oniguruma': 3.23.0 - '@shikijs/langs': 3.23.0 - '@shikijs/themes': 3.23.0 - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - shiki@4.0.2: dependencies: '@shikijs/core': 4.0.2 From 4102d0a8fd286f21f56128932a0a0377e8609acf Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sat, 18 Apr 2026 18:35:11 -0400 Subject: [PATCH 2/2] fix: Shiki lang aliases for markdown fences and @pierre/diffs - Patch resolveLanguage: LANG_ALIASES + canonical id before ini fallback; resolved grammar uses canonical name. - Patched @pierre/diffs package.json: declare @shikijs/core, @shikijs/langs, shiki ^4, @shikijs/transformers ^4 (subpaths use the langs package, not per-grammar npm deps). - FENCE_LANG_ALIASES: yml, gql, md (yaml/graphql/markdown already canonical). --- packages/ui/src/components/markdown.tsx | 3 ++ patches/@pierre__diffs@1.1.12.patch | 66 +++++++++++++++++++++++-- pnpm-lock.yaml | 6 +-- 3 files changed, 68 insertions(+), 7 deletions(-) diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 816b8ba..fc3d95f 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -31,6 +31,9 @@ const FENCE_LANG_ALIASES: Record = { rb: "ruby", kt: "kotlin", cs: "csharp", + yml: "yaml", + gql: "graphql", + md: "markdown", }; // Eagerly start loading the highlighter at module level (client-only to avoid diff --git a/patches/@pierre__diffs@1.1.12.patch b/patches/@pierre__diffs@1.1.12.patch index e7596be..86e6e08 100644 --- a/patches/@pierre__diffs@1.1.12.patch +++ b/patches/@pierre__diffs@1.1.12.patch @@ -1,8 +1,8 @@ diff --git a/dist/highlighter/languages/resolveLanguage.js b/dist/highlighter/languages/resolveLanguage.js -index f5dff39f418566d009c46b31601d39f7a1e9c19b..eed7279e038551ec2cfd600afbd170510991b243 100644 +index f5dff39f418566d009c46b31601d39f7a1e9c19b..03dc7c8c83f2391694485d1363db7ee89bf5f79a 100644 --- a/dist/highlighter/languages/resolveLanguage.js +++ b/dist/highlighter/languages/resolveLanguage.js -@@ -1,16 +1,52 @@ +@@ -1,19 +1,89 @@ -import { RegisteredCustomLanguages, ResolvedLanguages, ResolvingLanguages } from "./constants.js"; import { isWorkerContext } from "../../utils/isWorkerContext.js"; -import { bundledLanguages } from "shiki"; @@ -44,6 +44,39 @@ index f5dff39f418566d009c46b31601d39f7a1e9c19b..eed7279e038551ec2cfd600afbd17051 + ini: () => import("@shikijs/langs/ini"), +}; + ++/** Map common short ids / aliases to keys in trimmedBundledLanguages (before ini fallback). */ ++const LANG_ALIASES = { ++ js: "javascript", ++ mjs: "javascript", ++ cjs: "javascript", ++ ts: "typescript", ++ mts: "typescript", ++ cts: "typescript", ++ py: "python", ++ pyw: "python", ++ rb: "ruby", ++ erb: "ruby", ++ gql: "graphql", ++ md: "markdown", ++ mdx: "markdown", ++ yml: "yaml", ++ eyaml: "yaml", ++ "c++": "cpp", ++ cxx: "cpp", ++ cc: "cpp", ++ hpp: "cpp", ++ hh: "cpp", ++ sh: "shellscript", ++ shell: "shellscript", ++ docker: "dockerfile", ++}; ++ ++function canonicalLangId(lang) { ++ const mapped = LANG_ALIASES[lang]; ++ if (mapped != null) return mapped; ++ return lang; ++} ++ +const FALLBACK_LANG = "ini"; + async function resolveLanguage(lang) { @@ -51,14 +84,19 @@ index f5dff39f418566d009c46b31601d39f7a1e9c19b..eed7279e038551ec2cfd600afbd17051 const resolver = ResolvingLanguages.get(lang); if (resolver != null) return resolver; try { ++ const id = canonicalLangId(lang); let loader = RegisteredCustomLanguages.get(lang); - if (loader == null && Object.prototype.hasOwnProperty.call(bundledLanguages, lang)) loader = bundledLanguages[lang]; - if (loader == null) throw new Error(`resolveLanguage: "${lang}" not found in bundled or custom languages`); -+ if (loader == null && Object.prototype.hasOwnProperty.call(trimmedBundledLanguages, lang)) loader = trimmedBundledLanguages[lang]; ++ if (loader == null && Object.prototype.hasOwnProperty.call(trimmedBundledLanguages, id)) loader = trimmedBundledLanguages[id]; + if (loader == null) loader = trimmedBundledLanguages[FALLBACK_LANG]; const resolver$1 = loader().then(({ default: data }) => { const resolvedLang = { - name: lang, +- name: lang, ++ name: id, + data + }; + if (!ResolvedLanguages.has(lang)) ResolvedLanguages.set(lang, resolvedLang); diff --git a/dist/highlighter/shared_highlighter.js b/dist/highlighter/shared_highlighter.js index ae0e90172df19fddd8ae8bcb7feef9fb92d5c3a6..c817426868f0d6b52cf62ad58b06665779be0a04 100644 --- a/dist/highlighter/shared_highlighter.js @@ -139,3 +177,23 @@ index f7de46b0a1611e6b90d8c647f99867ad3388b6ae..c25ee86344ad875414044a86ee7d9dbb //#region src/utils/createSpanNodeFromToken.ts function createSpanFromToken(token) { +diff --git a/package.json b/package.json +index e86a735b6fb4cc07048ba30517caced51ec5ec3c..681560dbd5a8d7d1cd93120cd0ef8d84e6343617 100644 +--- a/package.json ++++ b/package.json +@@ -63,11 +63,13 @@ + }, + "dependencies": { + "@pierre/theme": "0.0.28", +- "@shikijs/transformers": "^3.0.0", ++ "@shikijs/core": "4.0.2", ++ "@shikijs/langs": "4.0.2", ++ "@shikijs/transformers": "^4.0.0", + "diff": "8.0.3", + "hast-util-to-html": "9.0.5", + "lru_map": "0.4.1", +- "shiki": "^3.0.0" ++ "shiki": "^4.0.2" + }, + "devDependencies": { + "@arethetypeswrong/core": "0.18.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e78145d..bcfeb29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ overrides: patchedDependencies: '@pierre/diffs@1.1.12': - hash: 550aa11e6d89777306d9713e0fd0e671f149bff7bd2649ddbc1e72247244eb7a + hash: 1c39efc1f6750a414b433733422874766964595227df55535d1c92a7cc27052f path: patches/@pierre__diffs@1.1.12.patch importers: @@ -52,7 +52,7 @@ importers: version: 0.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@pierre/diffs': specifier: ^1.1.12 - version: 1.1.12(patch_hash=550aa11e6d89777306d9713e0fd0e671f149bff7bd2649ddbc1e72247244eb7a)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.1.12(patch_hash=1c39efc1f6750a414b433733422874766964595227df55535d1c92a7cc27052f)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tailwindcss/vite': specifier: ^4.1.18 version: 4.2.2(vite@7.3.2(@types/node@22.19.17)(jiti@2.6.1)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.8.3)) @@ -6130,7 +6130,7 @@ snapshots: '@opentelemetry/semantic-conventions@1.40.0': {} - '@pierre/diffs@1.1.12(patch_hash=550aa11e6d89777306d9713e0fd0e671f149bff7bd2649ddbc1e72247244eb7a)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@pierre/diffs@1.1.12(patch_hash=1c39efc1f6750a414b433733422874766964595227df55535d1c92a7cc27052f)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@pierre/theme': 0.0.28 '@shikijs/transformers': 4.0.2