From 06b768e35873cc9240d5635cae16fc20fc143a55 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Thu, 5 Mar 2026 11:30:48 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20Polish=20review=20tab=20=E2=80=94=20vie?= =?UTF-8?q?wed=20tracking,=20file=20tree,=20syntax=20highlighting,=20bette?= =?UTF-8?q?r=20UX?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add file "viewed" checkmarks with progress tracking (X/26 viewed in header + sidebar) - Add directory-grouped file tree in sidebar with review progress bar - Add sticky file headers that stay visible when scrolling through long diffs - Add file change type badges (NEW/DELETED/RENAMED) with colored left borders - Add syntax highlighting via shiki with lazy loading and progressive enhancement - Add merge confirmation dropdown showing unresolved comments + viewed progress - Make Approve & Merge button prominently green, Request Changes styled with error tokens - Show full branch names instead of aggressively truncated text - Add always-visible comment dots on lines with comments, subtle hover on others - Improve hunk headers with two-tone @@ display and context function highlighting - Add review progress bar to sidebar with file-level viewed state --- apps/web/src/components/review/DiffViewer.tsx | 25 +- apps/web/src/components/review/FileCard.tsx | 95 ++- apps/web/src/components/review/HunkRows.tsx | 29 +- .../components/review/LineWithComments.tsx | 50 +- .../src/components/review/ReviewHeader.tsx | 140 ++++- .../src/components/review/ReviewSidebar.tsx | 193 ++++-- apps/web/src/components/review/ReviewTab.tsx | 30 +- apps/web/src/components/review/parse-diff.ts | 22 +- apps/web/src/components/review/types.ts | 3 + .../components/review/use-syntax-highlight.ts | 163 +++++ package-lock.json | 559 ++++++++++++++++++ package.json | 1 + 12 files changed, 1207 insertions(+), 103 deletions(-) create mode 100644 apps/web/src/components/review/use-syntax-highlight.ts diff --git a/apps/web/src/components/review/DiffViewer.tsx b/apps/web/src/components/review/DiffViewer.tsx index 9ac4ac4..5cec6c5 100644 --- a/apps/web/src/components/review/DiffViewer.tsx +++ b/apps/web/src/components/review/DiffViewer.tsx @@ -12,6 +12,9 @@ interface DiffViewerProps { ) => void; onResolveComment: (commentId: string) => void; onUnresolveComment: (commentId: string) => void; + viewedFiles?: Set; + onToggleViewed?: (filePath: string) => void; + onRegisterRef?: (filePath: string, el: HTMLDivElement | null) => void; } export function DiffViewer({ @@ -20,18 +23,24 @@ export function DiffViewer({ onAddComment, onResolveComment, onUnresolveComment, + viewedFiles, + onToggleViewed, + onRegisterRef, }: DiffViewerProps) { return (
{files.map((file) => ( - c.filePath === file.newPath)} - onAddComment={onAddComment} - onResolveComment={onResolveComment} - onUnresolveComment={onUnresolveComment} - /> +
onRegisterRef?.(file.newPath, el)}> + c.filePath === file.newPath)} + onAddComment={onAddComment} + onResolveComment={onResolveComment} + onUnresolveComment={onUnresolveComment} + isViewed={viewedFiles?.has(file.newPath) ?? false} + onToggleViewed={() => onToggleViewed?.(file.newPath)} + /> +
))}
); diff --git a/apps/web/src/components/review/FileCard.tsx b/apps/web/src/components/review/FileCard.tsx index 3035424..a1180e3 100644 --- a/apps/web/src/components/review/FileCard.tsx +++ b/apps/web/src/components/review/FileCard.tsx @@ -1,8 +1,45 @@ -import { useState } from "react"; -import { ChevronDown, ChevronRight, Plus, Minus } from "lucide-react"; +import { useState, useMemo } from "react"; +import { + ChevronDown, + ChevronRight, + Plus, + Minus, + CheckCircle2, + Circle, +} from "lucide-react"; import { Badge } from "@/components/ui/badge"; -import type { FileDiff, DiffLine, ReviewComment } from "./types"; +import type { FileDiff, FileChangeType, DiffLine, ReviewComment } from "./types"; import { HunkRows } from "./HunkRows"; +import { useHighlightedFile } from "./use-syntax-highlight"; + +const changeTypeBadge: Record< + FileChangeType, + { label: string; classes: string } | null +> = { + added: { + label: "NEW", + classes: + "bg-status-success-bg text-status-success-fg border-status-success-border", + }, + deleted: { + label: "DELETED", + classes: + "bg-status-error-bg text-status-error-fg border-status-error-border", + }, + renamed: { + label: "RENAMED", + classes: + "bg-status-active-bg text-status-active-fg border-status-active-border", + }, + modified: null, +}; + +const leftBorderClass: Record = { + added: "border-l-2 border-l-status-success-fg", + deleted: "border-l-2 border-l-status-error-fg", + renamed: "border-l-2 border-l-status-active-fg", + modified: "border-l-2 border-l-primary/40", +}; interface FileCardProps { file: FileDiff; @@ -15,6 +52,8 @@ interface FileCardProps { ) => void; onResolveComment: (commentId: string) => void; onUnresolveComment: (commentId: string) => void; + isViewed?: boolean; + onToggleViewed?: () => void; } export function FileCard({ @@ -23,15 +62,25 @@ export function FileCard({ onAddComment, onResolveComment, onUnresolveComment, + isViewed = false, + onToggleViewed = () => {}, }: FileCardProps) { const [expanded, setExpanded] = useState(true); const commentCount = comments.length; + const badge = changeTypeBadge[file.changeType]; + + // Flatten all hunk lines for syntax highlighting + const allLines = useMemo( + () => file.hunks.flatMap((h) => h.lines), + [file.hunks], + ); + const tokenMap = useHighlightedFile(file.newPath, allLines); return (
- {/* File header */} + {/* File header — sticky so it stays visible when scrolling */} + {lineComments.length > 0 ? ( +
+ +
+ ) : ( + + )} {/* Code content */}
             
               {prefix}
             
-            {line.content}
+            {tokens
+              ? tokens.map((token, i) => (
+                  
+                    {token.content}
+                  
+                ))
+              : line.content}
           
diff --git a/apps/web/src/components/review/ReviewHeader.tsx b/apps/web/src/components/review/ReviewHeader.tsx index b259aac..f1a98a5 100644 --- a/apps/web/src/components/review/ReviewHeader.tsx +++ b/apps/web/src/components/review/ReviewHeader.tsx @@ -1,3 +1,4 @@ +import { useState, useRef, useEffect } from "react"; import { Check, X, @@ -11,6 +12,9 @@ import { CircleDot, RotateCcw, ArrowRight, + Eye, + AlertCircle, + GitMerge, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; @@ -43,6 +47,8 @@ interface ReviewHeaderProps { onApprove: () => void; onRequestChanges: () => void; preview: PreviewState | null; + viewedCount?: number; + totalCount?: number; } export function ReviewHeader({ @@ -58,9 +64,31 @@ export function ReviewHeader({ onApprove, onRequestChanges, preview, + viewedCount, + totalCount, }: ReviewHeaderProps) { const totalAdditions = files.reduce((s, f) => s + f.additions, 0); const totalDeletions = files.reduce((s, f) => s + f.deletions, 0); + const [showConfirmation, setShowConfirmation] = useState(false); + const confirmRef = useRef(null); + + // Click-outside handler to dismiss confirmation + useEffect(() => { + if (!showConfirmation) return; + function handleClickOutside(e: MouseEvent) { + if ( + confirmRef.current && + !confirmRef.current.contains(e.target as Node) + ) { + setShowConfirmation(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [showConfirmation]); + + const viewed = viewedCount ?? 0; + const total = totalCount ?? 0; return (
@@ -108,14 +136,14 @@ export function ReviewHeader({ {sourceBranch && ( -
+
- - {truncateBranch(sourceBranch)} + + {sourceBranch} - - {truncateBranch(targetBranch)} + + {targetBranch}
)} @@ -136,8 +164,18 @@ export function ReviewHeader({
+ {/* Center: review progress */} + {total > 0 && ( +
+ + + {viewed}/{total} viewed + +
+ )} + {/* Right: preview + actions */} -
+
{/* Preview controls */} {preview && } @@ -148,22 +186,82 @@ export function ReviewHeader({ variant="outline" size="sm" onClick={onRequestChanges} - className="h-7 text-xs" + className="h-8 text-xs px-3 border-status-error-border/50 text-status-error-fg hover:bg-status-error-bg/50 hover:border-status-error-border" > Request Changes - +
+ + + {/* Merge confirmation dropdown */} + {showConfirmation && ( +
+

+ Ready to merge? +

+
+
+ + + 0 unresolved comments + +
+
+ + + {viewed}/{total} files viewed + +
+
+
+ + +
+
+ )} +
)} {status === "approved" && ( @@ -247,9 +345,3 @@ function PreviewControls({ preview }: { preview: PreviewState }) { ); } - -function truncateBranch(branch: string): string { - const parts = branch.split("/"); - if (parts.length <= 2) return branch; - return parts.slice(-2).join("/"); -} diff --git a/apps/web/src/components/review/ReviewSidebar.tsx b/apps/web/src/components/review/ReviewSidebar.tsx index 5a85118..47f8476 100644 --- a/apps/web/src/components/review/ReviewSidebar.tsx +++ b/apps/web/src/components/review/ReviewSidebar.tsx @@ -1,7 +1,8 @@ -import { useState } from "react"; +import { useMemo, useState } from "react"; import { MessageSquare, FileCode, + FolderOpen, Plus, Minus, Circle, @@ -21,6 +22,7 @@ interface ReviewSidebarProps { activeFiles: FileDiff[]; commits: CommitInfo[]; onSelectCommit: (hash: string | null) => void; + viewedFiles?: Set; } export function ReviewSidebar({ @@ -31,6 +33,7 @@ export function ReviewSidebar({ activeFiles, commits, onSelectCommit, + viewedFiles = new Set(), }: ReviewSidebarProps) { const [view, setView] = useState("files"); @@ -45,6 +48,7 @@ export function ReviewSidebar({ onFileClick={onFileClick} selectedCommit={selectedCommit} activeFiles={activeFiles} + viewedFiles={viewedFiles} /> ) : ( (); + + for (const file of files) { + const lastSlash = file.newPath.lastIndexOf("/"); + const directory = lastSlash >= 0 ? file.newPath.slice(0, lastSlash + 1) : ""; + const existing = groups.get(directory); + if (existing) { + existing.push(file); + } else { + groups.set(directory, [file]); + } + } + + // Sort directories alphabetically, sort files within each directory + const sorted = Array.from(groups.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([directory, dirFiles]) => ({ + directory, + files: dirFiles.sort((a, b) => { + const aName = a.newPath.slice(a.newPath.lastIndexOf("/") + 1); + const bName = b.newPath.slice(b.newPath.lastIndexOf("/") + 1); + return aName.localeCompare(bName); + }), + })); + + return sorted; +} + +function getFileName(path: string): string { + const lastSlash = path.lastIndexOf("/"); + return lastSlash >= 0 ? path.slice(lastSlash + 1) : path; +} + +const changeTypeDotColor: Record = { + added: "bg-status-success-fg", + deleted: "bg-status-error-fg", + renamed: "bg-status-active-fg", +}; + function FilesView({ files, comments, onFileClick, selectedCommit, activeFiles, + viewedFiles, }: { files: FileDiff[]; comments: ReviewComment[]; onFileClick: (filePath: string) => void; selectedCommit: string | null; activeFiles: FileDiff[]; + viewedFiles: Set; }) { const unresolvedCount = comments.filter((c) => !c.resolved).length; const resolvedCount = comments.filter((c) => c.resolved).length; const activeFilePaths = new Set(activeFiles.map((f) => f.newPath)); + const directoryGroups = useMemo(() => groupFilesByDirectory(files), [files]); + + const viewedCount = files.filter((f) => viewedFiles.has(f.newPath)).length; + const totalCount = files.length; + const progressPercent = totalCount > 0 ? (viewedCount / totalCount) * 100 : 0; + return (
+ {/* Review progress */} + {totalCount > 0 && ( +
+

+ Review Progress +

+
+
+
+ + {viewedCount}/{totalCount} files viewed + +
+ )} + {/* Comment summary */} {comments.length > 0 && (
@@ -161,8 +236,8 @@ function FilesView({
)} - {/* File list */} -
+ {/* Directory-grouped file tree */} +

Files {selectedCommit && ( @@ -171,46 +246,74 @@ function FilesView({ )}

- {files.map((file) => { - const fileCommentCount = comments.filter( - (c) => c.filePath === file.newPath, - ).length; - const isInView = activeFilePaths.has(file.newPath); - const dimmed = selectedCommit && !isInView; + {directoryGroups.map((group) => ( +
+ {/* Directory header */} + {group.directory && ( +
+ + {group.directory} +
+ )} + {/* Files in directory */} +
+ {group.files.map((file) => { + const fileCommentCount = comments.filter( + (c) => c.filePath === file.newPath, + ).length; + const isInView = activeFilePaths.has(file.newPath); + const dimmed = selectedCommit && !isInView; + const isViewed = viewedFiles.has(file.newPath); + const dotColor = changeTypeDotColor[file.changeType]; - return ( - - ); - })} + return ( + + ); + })} +
+
+ ))}
); @@ -304,12 +407,6 @@ function CommitsView({ /* ─── Helpers ──────────────────────────────────────────── */ -function formatFilePath(path: string): string { - const parts = path.split("/"); - if (parts.length <= 2) return path; - return parts.slice(-2).join("/"); -} - function truncateMessage(msg: string): string { const firstLine = msg.split("\n")[0]; return firstLine.length > 50 ? firstLine.slice(0, 47) + "..." : firstLine; diff --git a/apps/web/src/components/review/ReviewTab.tsx b/apps/web/src/components/review/ReviewTab.tsx index 410c41a..049e1d6 100644 --- a/apps/web/src/components/review/ReviewTab.tsx +++ b/apps/web/src/components/review/ReviewTab.tsx @@ -15,8 +15,29 @@ interface ReviewTabProps { export function ReviewTab({ initiativeId }: ReviewTabProps) { const [status, setStatus] = useState("pending"); const [selectedCommit, setSelectedCommit] = useState(null); + const [viewedFiles, setViewedFiles] = useState>(new Set()); const fileRefs = useRef>(new Map()); + const toggleViewed = useCallback((filePath: string) => { + setViewedFiles(prev => { + const next = new Set(prev); + if (next.has(filePath)) { + next.delete(filePath); + } else { + next.add(filePath); + } + return next; + }); + }, []); + + const registerFileRef = useCallback((filePath: string, el: HTMLDivElement | null) => { + if (el) { + fileRefs.current.set(filePath, el); + } else { + fileRefs.current.delete(filePath); + } + }, []); + // Fetch phases for this initiative const phasesQuery = trpc.listPhases.useQuery({ initiativeId }); const pendingReviewPhases = useMemo( @@ -129,7 +150,7 @@ export function ReviewTab({ initiativeId }: ReviewTabProps) { lineType: c.lineType as "added" | "removed" | "context", body: c.body, author: c.author, - createdAt: c.createdAt instanceof Date ? c.createdAt.toISOString() : String(c.createdAt), + createdAt: typeof c.createdAt === 'string' ? c.createdAt : String(c.createdAt), resolved: c.resolved, })); }, [commentsQuery.data]); @@ -222,6 +243,7 @@ export function ReviewTab({ initiativeId }: ReviewTabProps) { setSelectedPhaseId(id); setSelectedCommit(null); setStatus("pending"); + setViewedFiles(new Set()); }, []); const unresolvedCount = comments.filter((c) => !c.resolved).length; @@ -261,6 +283,8 @@ export function ReviewTab({ initiativeId }: ReviewTabProps) { onApprove={handleApprove} onRequestChanges={handleRequestChanges} preview={previewState} + viewedCount={viewedFiles.size} + totalCount={allFiles.length} /> {/* Main content area — sidebar always rendered to preserve state */} @@ -285,6 +309,9 @@ export function ReviewTab({ initiativeId }: ReviewTabProps) { onAddComment={handleAddComment} onResolveComment={handleResolveComment} onUnresolveComment={handleUnresolveComment} + viewedFiles={viewedFiles} + onToggleViewed={toggleViewed} + onRegisterRef={registerFileRef} /> )}
@@ -300,6 +327,7 @@ export function ReviewTab({ initiativeId }: ReviewTabProps) { activeFiles={files} commits={commits} onSelectCommit={setSelectedCommit} + viewedFiles={viewedFiles} />
diff --git a/apps/web/src/components/review/parse-diff.ts b/apps/web/src/components/review/parse-diff.ts index 6773dd4..8a5697e 100644 --- a/apps/web/src/components/review/parse-diff.ts +++ b/apps/web/src/components/review/parse-diff.ts @@ -1,4 +1,4 @@ -import type { FileDiff, DiffHunk, DiffLine } from "./types"; +import type { FileDiff, FileChangeType, DiffHunk, DiffLine } from "./types"; /** * Parse a unified diff string into structured FileDiff objects. @@ -20,9 +20,13 @@ export function parseUnifiedDiff(raw: string): FileDiff[] { let additions = 0; let deletions = 0; + // Scan header lines (between "diff --git" and first "@@") for /dev/null markers + let hasOldDevNull = false; + let hasNewDevNull = false; let i = 1; - // Skip to first hunk header while (i < lines.length && !lines[i].startsWith("@@")) { + if (lines[i].startsWith("--- /dev/null")) hasOldDevNull = true; + if (lines[i].startsWith("+++ /dev/null")) hasNewDevNull = true; i++; } @@ -86,7 +90,19 @@ export function parseUnifiedDiff(raw: string): FileDiff[] { hunks.push({ header, oldStart, oldCount, newStart, newCount, lines: hunkLines }); } - files.push({ oldPath, newPath, hunks, additions, deletions }); + // Derive changeType from header markers and path comparison + let changeType: FileChangeType; + if (hasOldDevNull) { + changeType = "added"; + } else if (hasNewDevNull) { + changeType = "deleted"; + } else if (oldPath !== newPath) { + changeType = "renamed"; + } else { + changeType = "modified"; + } + + files.push({ oldPath, newPath, hunks, additions, deletions, changeType }); } return files; diff --git a/apps/web/src/components/review/types.ts b/apps/web/src/components/review/types.ts index a450d90..488a832 100644 --- a/apps/web/src/components/review/types.ts +++ b/apps/web/src/components/review/types.ts @@ -14,12 +14,15 @@ export interface DiffLine { newLineNumber: number | null; } +export type FileChangeType = 'added' | 'modified' | 'deleted' | 'renamed'; + export interface FileDiff { oldPath: string; newPath: string; hunks: DiffHunk[]; additions: number; deletions: number; + changeType: FileChangeType; } export interface ReviewComment { diff --git a/apps/web/src/components/review/use-syntax-highlight.ts b/apps/web/src/components/review/use-syntax-highlight.ts new file mode 100644 index 0000000..673591d --- /dev/null +++ b/apps/web/src/components/review/use-syntax-highlight.ts @@ -0,0 +1,163 @@ +import { useState, useEffect, useMemo } from "react"; +import type { ThemedToken } from "shiki"; + +/* ── Lazy singleton highlighter ─────────────────────────── */ + +let highlighterPromise: Promise +> | null> | null = null; + +const LANGS = [ + "typescript", + "javascript", + "tsx", + "jsx", + "json", + "css", + "html", + "yaml", + "markdown", + "bash", + "python", + "go", + "rust", + "sql", + "toml", + "xml", +] as const; + +function getHighlighter() { + if (!highlighterPromise) { + highlighterPromise = import("shiki") + .then(({ createHighlighter }) => + createHighlighter({ + themes: ["github-dark-default"], + langs: [...LANGS], + }), + ) + .catch(() => null); + } + return highlighterPromise; +} + +// Pre-warm on module load (non-blocking) +getHighlighter(); + +/* ── Language detection ──────────────────────────────────── */ + +const EXT_TO_LANG: Record = { + ts: "typescript", + tsx: "tsx", + js: "javascript", + jsx: "jsx", + mjs: "javascript", + cjs: "javascript", + json: "json", + css: "css", + scss: "css", + html: "html", + htm: "html", + yml: "yaml", + yaml: "yaml", + md: "markdown", + mdx: "markdown", + sh: "bash", + bash: "bash", + zsh: "bash", + py: "python", + go: "go", + rs: "rust", + sql: "sql", + toml: "toml", + xml: "xml", +}; + +function detectLang(path: string): string | null { + const ext = path.split(".").pop()?.toLowerCase() ?? ""; + return EXT_TO_LANG[ext] ?? null; +} + +/* ── Types ───────────────────────────────────────────────── */ + +export type TokenizedLine = ThemedToken[]; +/** Maps newLineNumber → highlighted tokens for that line */ +export type LineTokenMap = Map; + +interface DiffLineInput { + content: string; + newLineNumber: number | null; + type: "added" | "removed" | "context"; +} + +/* ── Hook ────────────────────────────────────────────────── */ + +/** + * Highlights the "new-side" content of a file diff. + * Returns null until highlighting is ready (progressive enhancement). + * Only context + added lines are highlighted (removed lines fall back to plain text). + */ +export function useHighlightedFile( + filePath: string, + allLines: DiffLineInput[], +): LineTokenMap | null { + const [tokenMap, setTokenMap] = useState(null); + + // Build stable code string + line number mapping + const { code, lineNums, cacheKey } = useMemo(() => { + const entries: { lineNum: number; content: string }[] = []; + for (const line of allLines) { + if ( + line.newLineNumber !== null && + (line.type === "context" || line.type === "added") + ) { + entries.push({ lineNum: line.newLineNumber, content: line.content }); + } + } + entries.sort((a, b) => a.lineNum - b.lineNum); + const c = entries.map((e) => e.content).join("\n"); + return { + code: c, + lineNums: entries.map((e) => e.lineNum), + cacheKey: `${filePath}:${c.length}:${entries.length}`, + }; + }, [filePath, allLines]); + + useEffect(() => { + const lang = detectLang(filePath); + if (!lang || !code) { + setTokenMap(null); + return; + } + + let cancelled = false; + + getHighlighter().then((highlighter) => { + if (cancelled || !highlighter) return; + + try { + const result = highlighter.codeToTokens(code, { + lang: lang as Parameters[1]["lang"], + theme: "github-dark-default", + }); + const map: LineTokenMap = new Map(); + + result.tokens.forEach((lineTokens: ThemedToken[], idx: number) => { + if (idx < lineNums.length) { + map.set(lineNums[idx], lineTokens); + } + }); + + if (!cancelled) setTokenMap(map); + } catch { + // Language not loaded or parse error — no highlighting + } + }); + + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [cacheKey]); + + return tokenMap; +} diff --git a/package-lock.json b/package-lock.json index 4bb3e07..1c49766 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "motion": "^12.34.5", "nanoid": "^5.1.6", "pino": "^10.3.0", + "shiki": "^4.0.1", "simple-git": "^3.30.0", "unique-names-generator": "^4.7.1", "zod": "^4.3.6" @@ -3974,6 +3975,106 @@ "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "license": "MIT" }, + "node_modules/@shikijs/core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.1.tgz", + "integrity": "sha512-vWvqi9JNgz1dRL9Nvog5wtx7RuNkf7MEPl2mU/cyUUxJeH1CAr3t+81h8zO8zs7DK6cKLMoU9TvukWIDjP4Lzg==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.0.1", + "@shikijs/types": "4.0.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.1.tgz", + "integrity": "sha512-DJK9NiwtGYqMuKCRO4Ip0FKNDQpmaiS+K5bFjJ7DWFn4zHueDWgaUG8kAofkrnXF6zPPYYQY7J5FYVW9MbZyBg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.1.tgz", + "integrity": "sha512-oCWdCTDch3J8Kc0OZJ98KuUPC02O1VqIE3W/e2uvrHqTxYRR21RGEJMtchrgrxhsoJJCzmIciKsqG+q/yD+Cxg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.1.tgz", + "integrity": "sha512-v/mluaybWdnGJR4GqAR6zh8qAZohW9k+cGYT28Y7M8+jLbC0l4yG085O1A+WkseHTn+awd+P3UBymb2+MXFc8w==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.1.tgz", + "integrity": "sha512-ns0hHZc5eWZuvuIEJz2pTx3Qecz0aRVYumVQJ8JgWY2tq/dH8WxdcVM49Fc2NsHEILNIT6vfdW9MF26RANWiTA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.1.tgz", + "integrity": "sha512-FW41C/D6j/yKQkzVdjrRPiJCtgeDaYRJFEyCKFCINuRJRj9WcmubhP4KQHPZ4+9eT87jruSrYPyoblNRyDFzvA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.1.tgz", + "integrity": "sha512-EaygPEn57+jJ76mw+nTLvIpJMAcMPokFbrF8lufsZP7Ukk+ToJYEcswN1G0e49nUZAq7aCQtoeW219A8HK1ZOw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", @@ -4905,6 +5006,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", @@ -4928,6 +5038,15 @@ "@types/mdurl": "^2" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", @@ -4961,6 +5080,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -4984,6 +5109,12 @@ "@types/node": "*" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, "node_modules/@vitejs/plugin-react": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", @@ -5514,6 +5645,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -5524,6 +5665,26 @@ "node": ">=18" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -5603,6 +5764,16 @@ "dev": true, "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -5715,6 +5886,15 @@ "node": ">=4.0.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -5730,6 +5910,19 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -6930,6 +7123,42 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", @@ -6944,6 +7173,16 @@ "dev": true, "license": "MIT" }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/human-signals": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", @@ -7341,6 +7580,27 @@ "node": ">= 20" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -7357,6 +7617,95 @@ "node": ">= 8" } }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -7728,6 +8077,23 @@ "wrappy": "1" } }, + "node_modules/oniguruma-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", + "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", + "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.1", + "regex": "^6.0.1", + "regex-recursion": "^6.0.2" + } + }, "node_modules/orderedmap": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", @@ -8176,6 +8542,16 @@ ], "license": "MIT" }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/prosemirror-changeset": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz", @@ -8608,6 +8984,30 @@ "node": ">= 4" } }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -8910,6 +9310,25 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.1.tgz", + "integrity": "sha512-EkAEhDTN5WhpoQFXFw79OHIrSAfHhlImeCdSyg4u4XvrpxKEmdo/9x/HWSowujAnUrFsGOwWiE58a6GVentMnQ==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.0.1", + "@shikijs/engine-javascript": "4.0.1", + "@shikijs/engine-oniguruma": "4.0.1", + "@shikijs/langs": "4.0.1", + "@shikijs/themes": "4.0.1", + "@shikijs/types": "4.0.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -9038,6 +9457,16 @@ "source-map": "^0.6.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -9076,6 +9505,20 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-bom-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", @@ -9402,6 +9845,16 @@ "node": ">=8.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -9493,6 +9946,74 @@ "node": ">=8" } }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unplugin": { "version": "2.3.11", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz", @@ -9598,6 +10119,34 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", @@ -9862,6 +10411,16 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "packages/shared": { "name": "@codewalk-district/shared", "version": "0.0.1" diff --git a/package.json b/package.json index 5e98f83..2c68830 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "motion": "^12.34.5", "nanoid": "^5.1.6", "pino": "^10.3.0", + "shiki": "^4.0.1", "simple-git": "^3.30.0", "unique-names-generator": "^4.7.1", "zod": "^4.3.6"