diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx index e3e4c94..a2ebee6 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx @@ -153,25 +153,6 @@ function IssueDetailPage() { - {/* Labels */} - {issue.labels.length > 0 && ( -
- {issue.labels.map((label) => ( - - {label.name} - - ))} -
- )} - {/* Body */} {issue.body ? (
@@ -292,6 +273,29 @@ function IssueDetailPage() { )} + {/* Labels */} + + {issue.labels.length > 0 ? ( +
+ {issue.labels.map((label) => ( + + {label.name} + + ))} +
+ ) : ( +

No labels

+ )} +
+ {/* Participants */} diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx index 94422b2..a8e1c57 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx @@ -1,9 +1,15 @@ import { - GitBranchIcon, + CalendarIcon, + ClockIcon, + CloseIcon, + CommentIcon, + FileIcon, + GitCommitIcon, GitMergeIcon, GitPullRequestClosedIcon, GitPullRequestDraftIcon, GitPullRequestIcon, + MessageIcon, } from "@quickhub/icons"; import { Markdown } from "@quickhub/ui/components/markdown"; import { Skeleton } from "@quickhub/ui/components/skeleton"; @@ -15,7 +21,7 @@ import { import { cn } from "@quickhub/ui/lib/utils"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { createFileRoute, Link } from "@tanstack/react-router"; -import { useState } from "react"; +import { useCallback, useRef, useState } from "react"; import { formatRelativeTime } from "#/components/pulls/pull-request-row"; import { updatePullBranch } from "#/lib/github.functions"; import { @@ -155,36 +161,30 @@ function PullDetailPage() {

{pr.title}

-
+
{stateConfig.label} {pr.author && ( - + <> {pr.author.login} - + {pr.author.login} - - wants to merge into{" "} - - {pr.baseRefName} - {" "} - from{" "} - - {pr.headRefName} - - - + wants to merge into + + from + + )}
@@ -192,29 +192,30 @@ function PullDetailPage() {
{/* Stats bar */} -
+
- + {pr.commits} {" "} {pr.commits === 1 ? "commit" : "commits"} · - + + {pr.changedFiles} {" "} {pr.changedFiles === 1 ? "file" : "files"} changed - · - + +{pr.additions} -{pr.deletions} +
@@ -376,26 +377,26 @@ function PullDetailPage() { {/* Details */}
- + {formatRelativeTime(pr.createdAt)} - + {formatRelativeTime(pr.updatedAt)} {pr.mergedAt && ( - + {formatRelativeTime(pr.mergedAt)} )} {pr.closedAt && !pr.mergedAt && ( - + {formatRelativeTime(pr.closedAt)} )} - + {pr.comments} - + {pr.reviewComments}
@@ -437,15 +438,20 @@ function ActorRow({ actor }: { actor: GitHubActor }) { } function DetailRow({ + icon: Icon, label, children, }: { + icon: React.FC<{ size?: number; strokeWidth?: number }>; label: string; children: React.ReactNode; }) { return (
- {label} + + + {label} + {children}
); @@ -661,6 +667,71 @@ function StatusRow({ ); } +const DIFF_BOX_COUNT = 5; + +function CopyBadge({ + value, + canTruncate, +}: { + value: string; + canTruncate?: boolean; +}) { + const [copied, setCopied] = useState(false); + const timeoutRef = useRef>(undefined); + + const handleClick = useCallback(() => { + navigator.clipboard.writeText(value); + setCopied(true); + clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => setCopied(false), 1500); + }, [value]); + + return ( + + + + + Copied! + + ); +} + +function DiffBoxes({ + additions, + deletions, +}: { + additions: number; + deletions: number; +}) { + const total = additions + deletions; + const greenCount = + total === 0 ? 0 : Math.round((additions / total) * DIFF_BOX_COUNT); + const redCount = total === 0 ? 0 : DIFF_BOX_COUNT - greenCount; + + const boxes: string[] = []; + for (let i = 0; i < greenCount; i++) boxes.push("bg-green-500"); + for (let i = 0; i < redCount; i++) boxes.push("bg-red-500"); + while (boxes.length < DIFF_BOX_COUNT) boxes.push("bg-muted-foreground/30"); + + return ( + + {boxes.map((color, i) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: static decorative boxes, order never changes + + ))} + + ); +} + function StatusDot({ color }: { color: string }) { return (
diff --git a/packages/icons/src/index.ts b/packages/icons/src/index.ts index b5d6a50..8dfcd28 100644 --- a/packages/icons/src/index.ts +++ b/packages/icons/src/index.ts @@ -5,20 +5,26 @@ export { AddCircleHalfDotIcon as IssuesIcon, BookOpen01Icon as BookOpenIcon, Bug01Icon as BugIcon, + Calendar01Icon as CalendarIcon, Cancel01Icon as CloseIcon, CheckListIcon as ReviewsIcon, + Clock01Icon as ClockIcon, CodeIcon, Comment01Icon as CommentIcon, ComputerIcon as SystemIcon, + Copy01Icon as CopyIcon, DashboardSquare01Icon as DashboardIcon, + File02Icon as FileIcon, FolderLibraryIcon, GitBranchIcon, + GitCommitIcon, GitMergeIcon, GitPullRequestClosedIcon, GitPullRequestDraftIcon, GitPullRequestIcon, Home01Icon as HomeIcon, InboxIcon, + Message01Icon as MessageIcon, Moon01Icon as MoonIcon, MoreHorizontalIcon, Notification01Icon as NotificationIcon, @@ -26,6 +32,7 @@ export { Settings01Icon as SettingsIcon, StarIcon, Sun01Icon as SunIcon, + Tick02Icon as TickIcon, ViewIcon, } from "@hugeicons/react"; export { GitHubLogo, GitHubWordmarkLogo } from "./brand-logos"; diff --git a/packages/ui/package.json b/packages/ui/package.json index d4cb9b0..e5b6082 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -16,8 +16,8 @@ "format": "biome format" }, "dependencies": { + "@fontsource-variable/geist-mono": "^5.2.7", "@fontsource-variable/inter": "^5.2.8", - "@fontsource/geist-mono": "^5.2.7", "@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-checkbox": "^1.1.4", @@ -35,6 +35,7 @@ "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toggle": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@shikijs/rehype": "^4.0.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.0", @@ -46,6 +47,7 @@ "react-resizable-panels": "^3.0.3", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.1", + "shiki": "^4.0.2", "sonner": "^2.0.1", "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7" diff --git a/packages/ui/src/components/markdown.tsx b/packages/ui/src/components/markdown.tsx index 2f64d16..7c65bd3 100644 --- a/packages/ui/src/components/markdown.tsx +++ b/packages/ui/src/components/markdown.tsx @@ -1,9 +1,330 @@ +import { Suspense, use, useCallback, useRef, useState } from "react"; import type { Components } from "react-markdown"; import ReactMarkdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import remarkGfm from "remark-gfm"; +import type { BundledLanguage, Highlighter, ThemeRegistrationRaw } from "shiki"; 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 vercelLightTokens: ThemeRegistrationRaw["tokenColors"] = [ + { + scope: ["comment", "punctuation.definition.comment"], + settings: { foreground: "#666666", fontStyle: "italic" }, + }, + { + scope: ["keyword", "storage", "storage.type", "storage.modifier"], + settings: { foreground: "#c41562" }, + }, + { + scope: ["string", "string.quoted", "string.template", "string.regexp"], + settings: { foreground: "#107d32" }, + }, + { + scope: [ + "constant", + "constant.numeric", + "constant.language", + "constant.character", + ], + settings: { foreground: "#005ff2" }, + }, + { + scope: ["entity.name.function", "support.function", "meta.function-call"], + settings: { foreground: "#7d00cc" }, + }, + { + scope: [ + "variable.parameter", + "meta.parameter", + "entity.name.variable.parameter", + ], + settings: { foreground: "#aa4d00" }, + }, + { + scope: [ + "variable.other.property", + "support.type.property-name", + "entity.name.tag", + "meta.object-literal.key", + ], + settings: { foreground: "#005ff2" }, + }, + { + scope: [ + "entity.name.type", + "entity.name.class", + "support.type", + "support.class", + ], + settings: { foreground: "#005ff2" }, + }, + { + scope: ["punctuation", "meta.brace", "meta.bracket"], + settings: { foreground: "#171717" }, + }, + { + scope: ["variable", "variable.other"], + settings: { foreground: "#171717" }, + }, + { + scope: [ + "entity.other.attribute-name", + "entity.other.attribute-name.jsx", + "entity.other.attribute-name.tsx", + ], + settings: { foreground: "#aa4d00" }, + }, + { + scope: ["markup.deleted", "punctuation.definition.deleted"], + settings: { foreground: "#c41562" }, + }, + { + scope: ["markup.inserted", "punctuation.definition.inserted"], + settings: { foreground: "#107d32" }, + }, +]; + +const vercelDarkTokens: ThemeRegistrationRaw["tokenColors"] = [ + { + scope: ["comment", "punctuation.definition.comment"], + settings: { foreground: "#a1a1a1", fontStyle: "italic" }, + }, + { + scope: ["keyword", "storage", "storage.type", "storage.modifier"], + settings: { foreground: "#ff4d8d" }, + }, + { + scope: ["string", "string.quoted", "string.template", "string.regexp"], + settings: { foreground: "#00ca50" }, + }, + { + scope: [ + "constant", + "constant.numeric", + "constant.language", + "constant.character", + ], + settings: { foreground: "#47a8ff" }, + }, + { + scope: ["entity.name.function", "support.function", "meta.function-call"], + settings: { foreground: "#c472fb" }, + }, + { + scope: [ + "variable.parameter", + "meta.parameter", + "entity.name.variable.parameter", + ], + settings: { foreground: "#ff9300" }, + }, + { + scope: [ + "variable.other.property", + "support.type.property-name", + "entity.name.tag", + "meta.object-literal.key", + ], + settings: { foreground: "#47a8ff" }, + }, + { + scope: [ + "entity.name.type", + "entity.name.class", + "support.type", + "support.class", + ], + settings: { foreground: "#47a8ff" }, + }, + { + scope: ["punctuation", "meta.brace", "meta.bracket"], + settings: { foreground: "#ededed" }, + }, + { + scope: ["variable", "variable.other"], + settings: { foreground: "#ededed" }, + }, + { + scope: [ + "entity.other.attribute-name", + "entity.other.attribute-name.jsx", + "entity.other.attribute-name.tsx", + ], + settings: { foreground: "#ff9300" }, + }, + { + scope: ["markup.deleted", "punctuation.definition.deleted"], + settings: { foreground: "#ff4d8d" }, + }, + { + scope: ["markup.inserted", "punctuation.definition.inserted"], + settings: { foreground: "#00ca50" }, + }, +]; + +const vercelLight: ThemeRegistrationRaw = { + name: "vercel-light", + type: "light", + settings: vercelLightTokens as ThemeRegistrationRaw["settings"], + colors: { + "editor.background": "#ffffff", + "editor.foreground": "#171717", + }, + tokenColors: vercelLightTokens, +}; + +const vercelDark: ThemeRegistrationRaw = { + name: "vercel-dark", + type: "dark", + settings: vercelDarkTokens as ThemeRegistrationRaw["settings"], + colors: { + "editor.background": "#1a1a1a", + "editor.foreground": "#ededed", + }, + tokenColors: vercelDarkTokens, +}; + +// Eagerly start loading the highlighter at module level +const highlighterPromise: Promise = import("shiki").then((shiki) => + shiki.createHighlighter({ + themes: [vercelLight, vercelDark], + langs: PRELOADED_LANGS, + }), +); + +const htmlCache = new Map>(); + +function highlightCode(code: string, lang: string): Promise { + const key = `${lang}:${code}`; + 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"; + } + } + return highlighter.codeToHtml(code, { + lang: effectiveLang, + themes: { light: "vercel-light", dark: "vercel-dark" }, + defaultColor: false, + }); + }); + htmlCache.set(key, promise); + return promise; +} + +function CopyButton({ code }: { code: string }) { + const [copied, setCopied] = useState(false); + const timeoutRef = useRef>(undefined); + + const handleClick = useCallback(() => { + navigator.clipboard.writeText(code); + setCopied(true); + clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => setCopied(false), 1500); + }, [code]); + + return ( + + ); +} + +function ShikiCodeInner({ code, lang }: { code: string; lang: string }) { + const html = use(highlightCode(code, lang)); + + return ( +
+ {/* biome-ignore lint/security/noDangerouslySetInnerHtml: shiki output is trusted */} +
+ +
+ ); +} + +function ShikiCode({ code, lang }: { code: string; lang: string }) { + return ( + +
+						{code}
+					
+
+ } + > + + + ); +} + const components: Components = { h1: ({ children, ...props }) => (

), code: ({ children, className, ...props }) => { - const isBlock = className?.includes("language-"); - if (isBlock) { - return ( - - {children} - - ); + const langMatch = className?.match(/language-(\w+)/); + if (langMatch) { + const code = String(children).replace(/\n$/, ""); + return ; } return ( ); }, - pre: ({ children, ...props }) => ( -
-			{children}
-		
- ), + pre: ({ children, node, ...props }) => { + const codeChild = node?.children?.[0]; + if ( + codeChild?.type === "element" && + codeChild.tagName === "code" && + Array.isArray(codeChild.properties?.className) && + (codeChild.properties.className as string[]).some((c) => + String(c).startsWith("language-"), + ) + ) { + return <>{children}; + } + return ( +
+				{children}
+			
+ ); + }, hr: (props) =>
, img: ({ alt, ...props }) => ( =20'} + + '@shikijs/engine-javascript@4.0.2': + resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.0.2': + resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==} + engines: {node: '>=20'} + + '@shikijs/langs@4.0.2': + resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.0.2': + resolution: {integrity: sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==} + engines: {node: '>=20'} + + '@shikijs/rehype@4.0.2': + resolution: {integrity: sha512-cmPlKLD8JeojasNFoY64162ScpEdEdQUMuVodPCrv1nx1z3bjmGwoKWDruQWa/ejSznImlaeB0Ty6Q3zPaVQAA==} + engines: {node: '>=20'} + + '@shikijs/themes@4.0.2': + resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==} + engines: {node: '>=20'} + + '@shikijs/types@4.0.2': + resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} @@ -3520,12 +3561,18 @@ packages: hast-util-raw@9.1.0: resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} hast-util-to-parse5@8.0.1: resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + hast-util-to-string@3.0.1: + resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} @@ -3965,6 +4012,12 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} + oniguruma-parser@0.12.1: + resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} + + oniguruma-to-es@4.3.5: + resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==} + parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} @@ -4117,6 +4170,15 @@ packages: resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} engines: {node: '>= 4'} + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} @@ -4205,6 +4267,10 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} + shiki@4.0.2: + resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==} + engines: {node: '>=20'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -5359,9 +5425,9 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@fontsource-variable/inter@5.2.8': {} + '@fontsource-variable/geist-mono@5.2.7': {} - '@fontsource/geist-mono@5.2.7': {} + '@fontsource-variable/inter@5.2.8': {} '@hugeicons/react@0.3.4(react@19.2.4)': dependencies: @@ -6425,6 +6491,55 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true + '@shikijs/core@4.0.2': + dependencies: + '@shikijs/primitive': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.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@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + + '@shikijs/primitive@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/rehype@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + '@types/hast': 3.0.4 + hast-util-to-string: 3.0.1 + shiki: 4.0.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + + '@shikijs/themes@4.0.2': + dependencies: + '@shikijs/types': 4.0.2 + + '@shikijs/types@4.0.2': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + '@sindresorhus/is@7.2.0': {} '@solid-primitives/event-listener@2.4.5(solid-js@1.9.12)': @@ -7558,6 +7673,20 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.8 @@ -7588,6 +7717,10 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 + hast-util-to-string@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 @@ -8236,6 +8369,14 @@ snapshots: dependencies: mimic-function: 5.0.1 + oniguruma-parser@0.12.1: {} + + oniguruma-to-es@4.3.5: + dependencies: + oniguruma-parser: 0.12.1 + regex: 6.1.0 + regex-recursion: 6.0.2 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -8393,6 +8534,16 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 @@ -8538,6 +8689,17 @@ snapshots: shell-quote@1.8.3: {} + shiki@4.0.2: + dependencies: + '@shikijs/core': 4.0.2 + '@shikijs/engine-javascript': 4.0.2 + '@shikijs/engine-oniguruma': 4.0.2 + '@shikijs/langs': 4.0.2 + '@shikijs/themes': 4.0.2 + '@shikijs/types': 4.0.2 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + siginfo@2.0.0: {} signal-exit@4.1.0: {}