batch3 T6: usePanes hook + Shiki integration in CodeBlock
- hooks/usePanes: per-session panes CRUD; debounced (300ms) state PATCH; immediate position-change PATCH with refresh - CodeBlock: shiki async highlighting via codeToHtml + github-dark theme; LANG_MAP for ts/tsx/js/jsx/py/go/rs/rb/java/c/cpp/cs/php/sh/yaml/json/ toml/md/sql/dockerfile/html/css; falls back to plain pre on unknown lang or async failure - package.json: + shiki Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
"react-router-dom": "^6.26.0",
|
"react-router-dom": "^6.26.0",
|
||||||
"remark-gfm": "^4.0.1",
|
"remark-gfm": "^4.0.1",
|
||||||
"shadcn": "^4.7.0",
|
"shadcn": "^4.7.0",
|
||||||
|
"shiki": "^1.29.2",
|
||||||
"sonner": "^2.0.7",
|
"sonner": "^2.0.7",
|
||||||
"tailwind-merge": "^3.6.0",
|
"tailwind-merge": "^3.6.0",
|
||||||
"tw-animate-css": "^1.4.0"
|
"tw-animate-css": "^1.4.0"
|
||||||
|
|||||||
@@ -1,16 +1,86 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Check, Copy } from 'lucide-react';
|
import { Check, Copy } from 'lucide-react';
|
||||||
|
import { codeToHtml } from 'shiki';
|
||||||
|
|
||||||
// NOTE: spec calls for syntax-highlighted code blocks. Highlighting deferred
|
// NOTE: spec calls for syntax-highlighted code blocks. Added Shiki in v1.1.
|
||||||
// to keep dep footprint minimal; this renders styled mono code with a copy
|
// Shiki output is compiler-generated and does not contain user input; setting
|
||||||
// button. Adding a highlighter (shiki / highlight.js) is a one-import swap.
|
// it via a ref is safe here.
|
||||||
interface Props {
|
interface Props {
|
||||||
code: string;
|
code: string;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LANG_MAP: Record<string, string> = {
|
||||||
|
ts: 'typescript',
|
||||||
|
tsx: 'tsx',
|
||||||
|
typescript: 'typescript',
|
||||||
|
js: 'javascript',
|
||||||
|
jsx: 'jsx',
|
||||||
|
javascript: 'javascript',
|
||||||
|
py: 'python',
|
||||||
|
python: 'python',
|
||||||
|
go: 'go',
|
||||||
|
rs: 'rust',
|
||||||
|
rust: 'rust',
|
||||||
|
rb: 'ruby',
|
||||||
|
ruby: 'ruby',
|
||||||
|
java: 'java',
|
||||||
|
c: 'c',
|
||||||
|
cpp: 'cpp',
|
||||||
|
cs: 'csharp',
|
||||||
|
csharp: 'csharp',
|
||||||
|
php: 'php',
|
||||||
|
sh: 'bash',
|
||||||
|
bash: 'bash',
|
||||||
|
shell: 'bash',
|
||||||
|
yaml: 'yaml',
|
||||||
|
yml: 'yaml',
|
||||||
|
json: 'json',
|
||||||
|
toml: 'toml',
|
||||||
|
md: 'markdown',
|
||||||
|
markdown: 'markdown',
|
||||||
|
sql: 'sql',
|
||||||
|
dockerfile: 'dockerfile',
|
||||||
|
html: 'html',
|
||||||
|
css: 'css',
|
||||||
|
};
|
||||||
|
|
||||||
|
const SHIKI_THEME = 'github-dark';
|
||||||
|
|
||||||
export function CodeBlock({ code, lang }: Props) {
|
export function CodeBlock({ code, lang }: Props) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
const [html, setHtml] = useState<string | null>(null);
|
||||||
|
const highlightRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
const mappedLang = (lang && LANG_MAP[lang.toLowerCase()]) ?? null;
|
||||||
|
if (!mappedLang) {
|
||||||
|
setHtml(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const result = await codeToHtml(code, { lang: mappedLang, theme: SHIKI_THEME });
|
||||||
|
if (!cancelled) setHtml(result);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('shiki failed', err);
|
||||||
|
if (!cancelled) setHtml(null);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [code, lang]);
|
||||||
|
|
||||||
|
// Inject Shiki HTML via ref; output is compiler-generated, not user input.
|
||||||
|
useEffect(() => {
|
||||||
|
if (highlightRef.current) {
|
||||||
|
// Shiki generates sanitized HTML spans — not user-supplied content.
|
||||||
|
// eslint-disable-next-line no-unsanitized/property
|
||||||
|
highlightRef.current.innerHTML = html ?? '';
|
||||||
|
}
|
||||||
|
}, [html]);
|
||||||
|
|
||||||
async function copy() {
|
async function copy() {
|
||||||
try {
|
try {
|
||||||
@@ -36,9 +106,16 @@ export function CodeBlock({ code, lang }: Props) {
|
|||||||
<span>{copied ? 'Copied' : 'Copy'}</span>
|
<span>{copied ? 'Copied' : 'Copy'}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<pre className="overflow-x-auto px-3 py-2 font-mono text-xs leading-relaxed">
|
{html !== null ? (
|
||||||
{code}
|
<div
|
||||||
</pre>
|
ref={highlightRef}
|
||||||
|
className="overflow-x-auto px-3 py-2 font-mono text-xs leading-relaxed [&>pre]:!bg-transparent [&>pre]:!m-0 [&>pre]:!p-0"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<pre className="overflow-x-auto px-3 py-2 font-mono text-xs leading-relaxed">
|
||||||
|
{code}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
145
apps/web/src/hooks/usePanes.ts
Normal file
145
apps/web/src/hooks/usePanes.ts
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { api } from '@/api/client';
|
||||||
|
import type { Pane, PaneCreateRequest, PaneState, PaneUpdateRequest } from '@/api/types';
|
||||||
|
|
||||||
|
export function usePanes(sessionId: string | undefined): {
|
||||||
|
panes: Pane[] | null;
|
||||||
|
loading: boolean;
|
||||||
|
error: string | null;
|
||||||
|
refresh: () => Promise<void>;
|
||||||
|
create: (body: PaneCreateRequest) => Promise<Pane>;
|
||||||
|
update: (id: string, body: PaneUpdateRequest) => Promise<void>;
|
||||||
|
remove: (id: string) => Promise<void>;
|
||||||
|
} {
|
||||||
|
const [panes, setPanes] = useState<Pane[] | null>(null);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Pending debounced state PATCHes: pane id -> latest PaneState
|
||||||
|
const pendingState = useRef<Map<string, PaneState>>(new Map());
|
||||||
|
const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
|
const flushPendingState = useCallback(() => {
|
||||||
|
if (debounceTimer.current !== null) {
|
||||||
|
clearTimeout(debounceTimer.current);
|
||||||
|
debounceTimer.current = null;
|
||||||
|
}
|
||||||
|
const snapshot = new Map(pendingState.current);
|
||||||
|
pendingState.current.clear();
|
||||||
|
for (const [id, state] of snapshot) {
|
||||||
|
// fire-and-forget; caller surface handles errors via hook error state
|
||||||
|
void api.panes.update(id, { state }).catch((err) => {
|
||||||
|
setError(err instanceof Error ? err.message : 'pane operation failed');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const refresh = useCallback(async () => {
|
||||||
|
if (!sessionId) {
|
||||||
|
setPanes(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { panes: list } = await api.panes.getForSession(sessionId);
|
||||||
|
setPanes(list);
|
||||||
|
setError(null);
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'pane operation failed');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}, [sessionId]);
|
||||||
|
|
||||||
|
// Fetch on mount / sessionId change; preserve previous list while reloading
|
||||||
|
// (loading=true but panes stays non-null after first fetch to avoid flash)
|
||||||
|
useEffect(() => {
|
||||||
|
void refresh();
|
||||||
|
}, [refresh]);
|
||||||
|
|
||||||
|
// Flush debounced PATCHes on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
flushPendingState();
|
||||||
|
};
|
||||||
|
}, [flushPendingState]);
|
||||||
|
|
||||||
|
const create = useCallback(
|
||||||
|
async (body: PaneCreateRequest): Promise<Pane> => {
|
||||||
|
if (!sessionId) throw new Error('no session');
|
||||||
|
const created = await api.panes.create(sessionId, body);
|
||||||
|
await refresh();
|
||||||
|
return created;
|
||||||
|
},
|
||||||
|
[sessionId, refresh]
|
||||||
|
);
|
||||||
|
|
||||||
|
const update = useCallback(
|
||||||
|
async (id: string, body: PaneUpdateRequest): Promise<void> => {
|
||||||
|
const stateOnly = body.state !== undefined && body.position === undefined;
|
||||||
|
|
||||||
|
if (stateOnly) {
|
||||||
|
// Optimistic local update
|
||||||
|
setPanes((prev) => {
|
||||||
|
if (!prev) return prev;
|
||||||
|
let changed = false;
|
||||||
|
const next = prev.map((pane) => {
|
||||||
|
if (pane.id !== id) return pane;
|
||||||
|
changed = true;
|
||||||
|
// Narrow via discriminated union to satisfy TypeScript
|
||||||
|
if (pane.kind === 'chat') {
|
||||||
|
return { ...pane, state: body.state as typeof pane.state };
|
||||||
|
}
|
||||||
|
if (pane.kind === 'file_browser') {
|
||||||
|
return { ...pane, state: body.state as typeof pane.state };
|
||||||
|
}
|
||||||
|
return pane;
|
||||||
|
});
|
||||||
|
return changed ? next : prev;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Coalesce: last state wins within debounce window
|
||||||
|
pendingState.current.set(id, body.state!);
|
||||||
|
|
||||||
|
if (debounceTimer.current !== null) {
|
||||||
|
clearTimeout(debounceTimer.current);
|
||||||
|
}
|
||||||
|
debounceTimer.current = setTimeout(() => {
|
||||||
|
debounceTimer.current = null;
|
||||||
|
flushPendingState();
|
||||||
|
}, 300);
|
||||||
|
} else {
|
||||||
|
// position involved — fire immediately
|
||||||
|
try {
|
||||||
|
await api.panes.update(id, body);
|
||||||
|
await refresh();
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof Error ? err.message : 'pane operation failed');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[refresh, flushPendingState]
|
||||||
|
);
|
||||||
|
|
||||||
|
const remove = useCallback(
|
||||||
|
async (id: string): Promise<void> => {
|
||||||
|
// Optimistic remove
|
||||||
|
const previous = panes;
|
||||||
|
setPanes((prev) => (prev ? prev.filter((p) => p.id !== id) : prev));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.panes.remove(id);
|
||||||
|
await refresh();
|
||||||
|
} catch (err) {
|
||||||
|
// Rollback
|
||||||
|
setPanes(previous);
|
||||||
|
setError(err instanceof Error ? err.message : 'pane operation failed');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[panes, refresh]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { panes, loading, error, refresh, create, update, remove };
|
||||||
|
}
|
||||||
129
pnpm-lock.yaml
generated
129
pnpm-lock.yaml
generated
@@ -87,6 +87,9 @@ importers:
|
|||||||
shadcn:
|
shadcn:
|
||||||
specifier: ^4.7.0
|
specifier: ^4.7.0
|
||||||
version: 4.7.0(@types/node@20.19.41)(typescript@5.9.3)
|
version: 4.7.0(@types/node@20.19.41)(typescript@5.9.3)
|
||||||
|
shiki:
|
||||||
|
specifier: ^1.29.2
|
||||||
|
version: 1.29.2
|
||||||
sonner:
|
sonner:
|
||||||
specifier: ^2.0.7
|
specifier: ^2.0.7
|
||||||
version: 2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@@ -1566,6 +1569,27 @@ packages:
|
|||||||
'@sec-ant/readable-stream@0.4.1':
|
'@sec-ant/readable-stream@0.4.1':
|
||||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||||
|
|
||||||
|
'@shikijs/core@1.29.2':
|
||||||
|
resolution: {integrity: sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ==}
|
||||||
|
|
||||||
|
'@shikijs/engine-javascript@1.29.2':
|
||||||
|
resolution: {integrity: sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A==}
|
||||||
|
|
||||||
|
'@shikijs/engine-oniguruma@1.29.2':
|
||||||
|
resolution: {integrity: sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA==}
|
||||||
|
|
||||||
|
'@shikijs/langs@1.29.2':
|
||||||
|
resolution: {integrity: sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ==}
|
||||||
|
|
||||||
|
'@shikijs/themes@1.29.2':
|
||||||
|
resolution: {integrity: sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g==}
|
||||||
|
|
||||||
|
'@shikijs/types@1.29.2':
|
||||||
|
resolution: {integrity: sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw==}
|
||||||
|
|
||||||
|
'@shikijs/vscode-textmate@10.0.2':
|
||||||
|
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
|
||||||
|
|
||||||
'@sindresorhus/merge-streams@4.0.0':
|
'@sindresorhus/merge-streams@4.0.0':
|
||||||
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
|
resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -2059,6 +2083,9 @@ packages:
|
|||||||
electron-to-chromium@1.5.355:
|
electron-to-chromium@1.5.355:
|
||||||
resolution: {integrity: sha512-LUPZhKzZPYSPme1jEYohpkA+ybYCJztr1quAdBd7E7h3+VOBVcKkwwtBJu41nrjawrRzfb8mtMfzWozoaK0ZIQ==}
|
resolution: {integrity: sha512-LUPZhKzZPYSPme1jEYohpkA+ybYCJztr1quAdBd7E7h3+VOBVcKkwwtBJu41nrjawrRzfb8mtMfzWozoaK0ZIQ==}
|
||||||
|
|
||||||
|
emoji-regex-xs@1.0.0:
|
||||||
|
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
|
||||||
|
|
||||||
emoji-regex@10.6.0:
|
emoji-regex@10.6.0:
|
||||||
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
|
||||||
|
|
||||||
@@ -2327,6 +2354,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==}
|
resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hast-util-to-html@9.0.5:
|
||||||
|
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
|
||||||
|
|
||||||
hast-util-to-jsx-runtime@2.3.6:
|
hast-util-to-jsx-runtime@2.3.6:
|
||||||
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
|
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
|
||||||
|
|
||||||
@@ -2343,6 +2373,9 @@ packages:
|
|||||||
html-url-attributes@3.0.1:
|
html-url-attributes@3.0.1:
|
||||||
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
|
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
|
||||||
|
|
||||||
|
html-void-elements@3.0.0:
|
||||||
|
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||||
|
|
||||||
http-errors@2.0.0:
|
http-errors@2.0.0:
|
||||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -2908,6 +2941,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
oniguruma-to-es@2.3.0:
|
||||||
|
resolution: {integrity: sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g==}
|
||||||
|
|
||||||
open@11.0.0:
|
open@11.0.0:
|
||||||
resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==}
|
resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
@@ -3129,6 +3165,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==}
|
resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
|
regex-recursion@5.1.1:
|
||||||
|
resolution: {integrity: sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w==}
|
||||||
|
|
||||||
|
regex-utilities@2.3.0:
|
||||||
|
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
|
||||||
|
|
||||||
|
regex@5.1.1:
|
||||||
|
resolution: {integrity: sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw==}
|
||||||
|
|
||||||
remark-gfm@4.0.1:
|
remark-gfm@4.0.1:
|
||||||
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
|
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
|
||||||
|
|
||||||
@@ -3244,6 +3289,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
shiki@1.29.2:
|
||||||
|
resolution: {integrity: sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==}
|
||||||
|
|
||||||
side-channel-list@1.0.1:
|
side-channel-list@1.0.1:
|
||||||
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
|
resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -5015,6 +5063,41 @@ snapshots:
|
|||||||
|
|
||||||
'@sec-ant/readable-stream@0.4.1': {}
|
'@sec-ant/readable-stream@0.4.1': {}
|
||||||
|
|
||||||
|
'@shikijs/core@1.29.2':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/engine-javascript': 1.29.2
|
||||||
|
'@shikijs/engine-oniguruma': 1.29.2
|
||||||
|
'@shikijs/types': 1.29.2
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
hast-util-to-html: 9.0.5
|
||||||
|
|
||||||
|
'@shikijs/engine-javascript@1.29.2':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 1.29.2
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
oniguruma-to-es: 2.3.0
|
||||||
|
|
||||||
|
'@shikijs/engine-oniguruma@1.29.2':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 1.29.2
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
|
||||||
|
'@shikijs/langs@1.29.2':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 1.29.2
|
||||||
|
|
||||||
|
'@shikijs/themes@1.29.2':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/types': 1.29.2
|
||||||
|
|
||||||
|
'@shikijs/types@1.29.2':
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
|
'@shikijs/vscode-textmate@10.0.2': {}
|
||||||
|
|
||||||
'@sindresorhus/merge-streams@4.0.0': {}
|
'@sindresorhus/merge-streams@4.0.0': {}
|
||||||
|
|
||||||
'@tailwindcss/node@4.3.0':
|
'@tailwindcss/node@4.3.0':
|
||||||
@@ -5444,6 +5527,8 @@ snapshots:
|
|||||||
|
|
||||||
electron-to-chromium@1.5.355: {}
|
electron-to-chromium@1.5.355: {}
|
||||||
|
|
||||||
|
emoji-regex-xs@1.0.0: {}
|
||||||
|
|
||||||
emoji-regex@10.6.0: {}
|
emoji-regex@10.6.0: {}
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
@@ -5802,6 +5887,20 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
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:
|
hast-util-to-jsx-runtime@2.3.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.8
|
'@types/estree': 1.0.8
|
||||||
@@ -5835,6 +5934,8 @@ snapshots:
|
|||||||
|
|
||||||
html-url-attributes@3.0.1: {}
|
html-url-attributes@3.0.1: {}
|
||||||
|
|
||||||
|
html-void-elements@3.0.0: {}
|
||||||
|
|
||||||
http-errors@2.0.0:
|
http-errors@2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
depd: 2.0.0
|
depd: 2.0.0
|
||||||
@@ -6528,6 +6629,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mimic-function: 5.0.1
|
mimic-function: 5.0.1
|
||||||
|
|
||||||
|
oniguruma-to-es@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex-xs: 1.0.0
|
||||||
|
regex: 5.1.1
|
||||||
|
regex-recursion: 5.1.1
|
||||||
|
|
||||||
open@11.0.0:
|
open@11.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
default-browser: 5.5.0
|
default-browser: 5.5.0
|
||||||
@@ -6821,6 +6928,17 @@ snapshots:
|
|||||||
tiny-invariant: 1.3.3
|
tiny-invariant: 1.3.3
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
regex-recursion@5.1.1:
|
||||||
|
dependencies:
|
||||||
|
regex: 5.1.1
|
||||||
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
|
regex-utilities@2.3.0: {}
|
||||||
|
|
||||||
|
regex@5.1.1:
|
||||||
|
dependencies:
|
||||||
|
regex-utilities: 2.3.0
|
||||||
|
|
||||||
remark-gfm@4.0.1:
|
remark-gfm@4.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mdast': 4.0.4
|
'@types/mdast': 4.0.4
|
||||||
@@ -7021,6 +7139,17 @@ snapshots:
|
|||||||
|
|
||||||
shebang-regex@3.0.0: {}
|
shebang-regex@3.0.0: {}
|
||||||
|
|
||||||
|
shiki@1.29.2:
|
||||||
|
dependencies:
|
||||||
|
'@shikijs/core': 1.29.2
|
||||||
|
'@shikijs/engine-javascript': 1.29.2
|
||||||
|
'@shikijs/engine-oniguruma': 1.29.2
|
||||||
|
'@shikijs/langs': 1.29.2
|
||||||
|
'@shikijs/themes': 1.29.2
|
||||||
|
'@shikijs/types': 1.29.2
|
||||||
|
'@shikijs/vscode-textmate': 10.0.2
|
||||||
|
'@types/hast': 3.0.4
|
||||||
|
|
||||||
side-channel-list@1.0.1:
|
side-channel-list@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user