feat: add fixed editorial presentation canvas

This commit is contained in:
lda
2026-07-05 17:38:41 +07:00 Verified
parent 7749a1820d
commit 905a604a74
13 changed files with 456 additions and 30 deletions
+3
View File
@@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@dagrejs/dagre": "3.0.0", "@dagrejs/dagre": "3.0.0",
"@fontsource-variable/newsreader": "5.2.10",
"@fontsource-variable/source-sans-3": "5.2.9", "@fontsource-variable/source-sans-3": "5.2.9",
"@fontsource/barlow-condensed": "5.2.8", "@fontsource/barlow-condensed": "5.2.8",
"@fontsource/ibm-plex-mono": "5.2.7", "@fontsource/ibm-plex-mono": "5.2.7",
@@ -22,6 +23,7 @@
"valibot": "1.4.2" "valibot": "1.4.2"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "4.3.2",
"@testing-library/jest-dom": "6.9.1", "@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "16.3.2", "@testing-library/react": "16.3.2",
"@testing-library/user-event": "14.6.1", "@testing-library/user-event": "14.6.1",
@@ -29,6 +31,7 @@
"@types/react-dom": "19.2.3", "@types/react-dom": "19.2.3",
"@vitejs/plugin-react": "6.0.3", "@vitejs/plugin-react": "6.0.3",
"jsdom": "29.1.1", "jsdom": "29.1.1",
"tailwindcss": "4.3.2",
"vite": "8.1.2", "vite": "8.1.2",
"vitest": "4.1.9" "vitest": "4.1.9"
} }
+1
View File
@@ -1,3 +1,4 @@
declare module "@fontsource-variable/newsreader/wght.css";
declare module "@fontsource-variable/source-sans-3"; declare module "@fontsource-variable/source-sans-3";
declare module "@fontsource/barlow-condensed/600.css"; declare module "@fontsource/barlow-condensed/600.css";
declare module "@fontsource/barlow-condensed/700.css"; declare module "@fontsource/barlow-condensed/700.css";
+2
View File
@@ -2,8 +2,10 @@ import { StrictMode } from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import "@fontsource/barlow-condensed/600.css"; import "@fontsource/barlow-condensed/600.css";
import "@fontsource/barlow-condensed/700.css"; import "@fontsource/barlow-condensed/700.css";
import "@fontsource-variable/newsreader/wght.css";
import "@fontsource-variable/source-sans-3"; import "@fontsource-variable/source-sans-3";
import "@fontsource/ibm-plex-mono/400.css"; import "@fontsource/ibm-plex-mono/400.css";
import "./presentation/styles/editorial.css";
import "./styles/global.css"; import "./styles/global.css";
import { App } from "./app/App.js"; import { App } from "./app/App.js";
@@ -0,0 +1,38 @@
import { act, cleanup, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, it } from "vitest";
import { PRESENTATION_HEIGHT, PRESENTATION_WIDTH } from "./canvas-fit.js";
import { PresentationCanvas } from "./PresentationCanvas.js";
const setViewport = (width: number, height: number) => {
Object.defineProperty(window, "innerWidth", { configurable: true, value: width });
Object.defineProperty(window, "innerHeight", { configurable: true, value: height });
};
afterEach(() => cleanup());
describe("PresentationCanvas", () => {
it("renders one fixed 1280x720 audience canvas", () => {
setViewport(1280, 720);
render(<PresentationCanvas><div>Scene</div></PresentationCanvas>);
const canvas = screen.getByTestId("presentation-canvas");
expect(canvas).toHaveStyle({
width: `${PRESENTATION_WIDTH}px`,
height: `${PRESENTATION_HEIGHT}px`,
transform: "scale(1)",
left: "0px",
top: "0px",
});
});
it("recomputes letterboxing after viewport resize", () => {
setViewport(1280, 720);
render(<PresentationCanvas><div>Scene</div></PresentationCanvas>);
setViewport(1024, 768);
act(() => window.dispatchEvent(new Event("resize")));
expect(screen.getByTestId("presentation-canvas")).toHaveStyle({
transform: "scale(0.8)",
left: "0px",
top: "96px",
});
});
});
@@ -0,0 +1,43 @@
import { useEffect, useState, type ReactNode } from "react";
import {
fitPresentationCanvas,
PRESENTATION_HEIGHT,
PRESENTATION_WIDTH,
type ViewportSize,
} from "./canvas-fit.js";
type PresentationCanvasProps = { readonly children: ReactNode };
const readViewport = (): ViewportSize => ({
width: window.innerWidth,
height: window.innerHeight,
});
// The fixed canvas deliberately prevents slide reflow; scenes always render
// inside a 1280×720 coordinate system and the viewport scales proportionally.
export const PresentationCanvas = ({ children }: PresentationCanvasProps) => {
const [viewport, setViewport] = useState(readViewport);
useEffect(() => {
const resize = () => setViewport(readViewport());
window.addEventListener("resize", resize);
return () => window.removeEventListener("resize", resize);
}, []);
const fit = fitPresentationCanvas(viewport);
return (
<div className="presentation-viewport">
<div
className="presentation-canvas"
data-testid="presentation-canvas"
style={{
width: PRESENTATION_WIDTH,
height: PRESENTATION_HEIGHT,
left: fit.offsetX,
top: fit.offsetY,
transform: `scale(${fit.scale})`,
}}
>
{children}
</div>
</div>
);
};
@@ -4,6 +4,7 @@ import { createPreparedRecipeDriver, assertNever } from "../demo/agent/preparedR
import { useDemoAgent } from "../demo/agent/useDemoAgent.js"; import { useDemoAgent } from "../demo/agent/useDemoAgent.js";
import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js"; import { loadCanonicalDemoRecording } from "../demo/timeline/replay.js";
import { useDemoTimeline } from "../demo/useDemoTimeline.js"; import { useDemoTimeline } from "../demo/useDemoTimeline.js";
import { PresentationCanvas } from "./PresentationCanvas.js";
import { PresentationStage } from "./PresentationStage.js"; import { PresentationStage } from "./PresentationStage.js";
import { PresenterControls } from "./PresenterControls.js"; import { PresenterControls } from "./PresenterControls.js";
import { ChatDock } from "./ChatDock.js"; import { ChatDock } from "./ChatDock.js";
@@ -185,21 +186,23 @@ export const PresentationRoute = () => {
return ( return (
<main className="presentation-route" aria-label="lda.chat presentation" data-motion={state.motionDisabled ? "disabled" : "enabled"}> <main className="presentation-route" aria-label="lda.chat presentation" data-motion={state.motionDisabled ? "disabled" : "enabled"}>
<PresentationStage <PresentationCanvas>
state={state} <PresentationStage
demo={demo} state={state}
evidence={evidence} demo={demo}
messages={agent.messages} evidence={evidence}
onApprove={agent.phase === "awaiting-approval" ? handleApprove : undefined} messages={agent.messages}
onDeny={agent.phase === "awaiting-approval" ? handleDeny : undefined} onApprove={agent.phase === "awaiting-approval" ? handleApprove : undefined}
jump={handleJump} onDeny={agent.phase === "awaiting-approval" ? handleDeny : undefined}
selectNode={(nodeId) => dispatch({ type: "select_node", nodeId })} jump={handleJump}
openEvidence={() => dispatch({ type: "set_evidence_mode", mode: "open" })} selectNode={(nodeId) => dispatch({ type: "select_node", nodeId })}
closeOverlay={() => dispatch({ type: "close_overlay" })} openEvidence={() => dispatch({ type: "set_evidence_mode", mode: "open" })}
openDiscussion={handleOpenDiscussion} closeOverlay={() => dispatch({ type: "close_overlay" })}
closeDiscussion={handleCloseDiscussion} openDiscussion={handleOpenDiscussion}
toggleDiscussionIndex={() => dispatch({ type: "toggle_discussion_index" })} closeDiscussion={handleCloseDiscussion}
/> toggleDiscussionIndex={() => dispatch({ type: "toggle_discussion_index" })}
/>
</PresentationCanvas>
{showChatDock && <ChatDock openChat={() => dispatch({ type: "set_chat_mode", mode: "rail" })} />} {showChatDock && <ChatDock openChat={() => dispatch({ type: "set_chat_mode", mode: "rail" })} />}
{state.controlsOpen && ( {state.controlsOpen && (
<PresenterControls <PresenterControls
@@ -0,0 +1,21 @@
import { describe, expect, it } from "vitest";
import { fitPresentationCanvas } from "./canvas-fit.js";
describe("fitPresentationCanvas", () => {
it.each([
[{ width: 1280, height: 720 }, { scale: 1, offsetX: 0, offsetY: 0 }],
[{ width: 1920, height: 1080 }, { scale: 1.5, offsetX: 0, offsetY: 0 }],
[{ width: 1024, height: 768 }, { scale: 0.8, offsetX: 0, offsetY: 96 }],
[{ width: 1600, height: 900 }, { scale: 1.25, offsetX: 0, offsetY: 0 }],
])("fits %o without reflow", (viewport, expected) => {
expect(fitPresentationCanvas(viewport)).toEqual(expected);
});
it("returns a bounded zero fit for an unavailable viewport", () => {
expect(fitPresentationCanvas({ width: 0, height: 0 })).toEqual({
scale: 0,
offsetX: 0,
offsetY: 0,
});
});
});
@@ -0,0 +1,28 @@
export const PRESENTATION_WIDTH = 1280;
export const PRESENTATION_HEIGHT = 720;
export type ViewportSize = {
readonly width: number;
readonly height: number;
};
export type CanvasFit = {
readonly scale: number;
readonly offsetX: number;
readonly offsetY: number;
};
export const fitPresentationCanvas = (viewport: ViewportSize): CanvasFit => {
if (viewport.width <= 0 || viewport.height <= 0) {
return { scale: 0, offsetX: 0, offsetY: 0 };
}
const scale = Math.min(
viewport.width / PRESENTATION_WIDTH,
viewport.height / PRESENTATION_HEIGHT,
);
return {
scale,
offsetX: (viewport.width - PRESENTATION_WIDTH * scale) / 2,
offsetY: (viewport.height - PRESENTATION_HEIGHT * scale) / 2,
};
};
@@ -0,0 +1,31 @@
@layer theme, base, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities);
@theme {
--font-editorial: "Newsreader Variable", Georgia, serif;
--font-interface: "Source Sans 3", sans-serif;
--font-evidence: "IBM Plex Mono", monospace;
--color-editorial-paper: oklch(0.975 0.012 82);
--color-editorial-ink: oklch(0.19 0.015 65);
--color-editorial-muted: oklch(0.48 0.025 65);
--color-intent: oklch(0.53 0.17 250);
--color-runtime: oklch(0.55 0.14 150);
--color-human: oklch(0.68 0.17 55);
}
.presentation-viewport {
position: fixed;
inset: 0;
overflow: hidden;
background: oklch(0.13 0.01 65);
}
.presentation-canvas {
position: absolute;
transform-origin: top left;
overflow: hidden;
background: var(--color-editorial-paper);
color: var(--color-editorial-ink);
font-family: var(--font-interface);
}
@@ -0,0 +1,17 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
const css = readFileSync(
join(import.meta.dirname, "editorial.css"),
"utf8",
);
describe("editorial Tailwind integration", () => {
it("loads theme and utilities without Preflight", () => {
expect(css).toContain('tailwindcss/theme.css');
expect(css).toContain('tailwindcss/utilities.css');
expect(css).not.toContain('tailwindcss/preflight.css');
expect(css).not.toContain('@import "tailwindcss"');
});
});
+1 -1
View File
@@ -6,7 +6,7 @@
"jsx": "react-jsx", "jsx": "react-jsx",
"lib": ["ES2023", "DOM", "DOM.Iterable"], "lib": ["ES2023", "DOM", "DOM.Iterable"],
"noEmit": true, "noEmit": true,
"types": ["vite/client", "vitest/globals"] "types": ["node", "vite/client", "vitest/globals"]
}, },
"include": ["src"] "include": ["src"]
} }
+2 -1
View File
@@ -1,10 +1,11 @@
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
import { defineConfig } from "vitest/config"; import { defineConfig } from "vitest/config";
const backendPort = process.env.WEB_PORT ?? "8787"; const backendPort = process.env.WEB_PORT ?? "8787";
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react(), tailwindcss()],
server: { server: {
proxy: { proxy: {
// Server must listen on this port (set via WEB_PORT env or default 8787) // Server must listen on this port (set via WEB_PORT env or default 8787)
+251 -13
View File
@@ -20,6 +20,9 @@ importers:
'@dagrejs/dagre': '@dagrejs/dagre':
specifier: 3.0.0 specifier: 3.0.0
version: 3.0.0 version: 3.0.0
'@fontsource-variable/newsreader':
specifier: 5.2.10
version: 5.2.10
'@fontsource-variable/source-sans-3': '@fontsource-variable/source-sans-3':
specifier: 5.2.9 specifier: 5.2.9
version: 5.2.9 version: 5.2.9
@@ -48,6 +51,9 @@ importers:
specifier: 1.4.2 specifier: 1.4.2
version: 1.4.2([email protected]) version: 1.4.2([email protected])
devDependencies: devDependencies:
'@tailwindcss/vite':
specifier: 4.3.2
version: 4.3.2([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
'@testing-library/jest-dom': '@testing-library/jest-dom':
specifier: 6.9.1 specifier: 6.9.1
version: 6.9.1 version: 6.9.1
@@ -65,16 +71,19 @@ importers:
version: 19.2.3(@types/[email protected]) version: 19.2.3(@types/[email protected])
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: 6.0.3 specifier: 6.0.3
version: 6.0.3([email protected](@types/[email protected])([email protected])([email protected])) version: 6.0.3([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
jsdom: jsdom:
specifier: 29.1.1 specifier: 29.1.1
version: 29.1.1 version: 29.1.1
tailwindcss:
specifier: 4.3.2
version: 4.3.2
vite: vite:
specifier: 8.1.2 specifier: 8.1.2
version: 8.1.2(@types/[email protected])([email protected])([email protected]) version: 8.1.2(@types/[email protected])([email protected])([email protected])([email protected])
vitest: vitest:
specifier: 4.1.9 specifier: 4.1.9
version: 4.1.9(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])) version: 4.1.9(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
apps/server: apps/server:
dependencies: dependencies:
@@ -99,7 +108,7 @@ importers:
version: 4.22.4 version: 4.22.4
vitest: vitest:
specifier: 4.1.9 specifier: 4.1.9
version: 4.1.9(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])) version: 4.1.9(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
packages/rpc: packages/rpc:
dependencies: dependencies:
@@ -118,7 +127,7 @@ importers:
version: 26.1.0 version: 26.1.0
vitest: vitest:
specifier: 4.1.9 specifier: 4.1.9
version: 4.1.9(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])) version: 4.1.9(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
packages: packages:
@@ -383,6 +392,9 @@ packages:
'@noble/hashes': '@noble/hashes':
optional: true optional: true
'@fontsource-variable/[email protected]':
resolution: {integrity: sha512-MdI2iRwrqWpMOU/2aV2BgfZ4dJNlj/XlYaY8Zb7t87mWqHskYW0XiUANkt1cyOCiEfW2VQ0bQ5vZgGPp6B2B4w==}
'@fontsource-variable/[email protected]': '@fontsource-variable/[email protected]':
resolution: {integrity: sha512-K9fkQbb0BNnRmmaU+Gpr6U4IaYKpNzA8t07J6aam2i+6v84RT72MVpar8OBr3yYAEznj4riKivPQ2nAF2fPKsw==} resolution: {integrity: sha512-K9fkQbb0BNnRmmaU+Gpr6U4IaYKpNzA8t07J6aam2i+6v84RT72MVpar8OBr3yYAEznj4riKivPQ2nAF2fPKsw==}
@@ -398,9 +410,22 @@ packages:
peerDependencies: peerDependencies:
hono: ^4 hono: ^4
'@jridgewell/[email protected]':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/[email protected]':
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
'@jridgewell/[email protected]':
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
engines: {node: '>=6.0.0'}
'@jridgewell/[email protected]': '@jridgewell/[email protected]':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
'@jridgewell/[email protected]':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@msgpackr-extract/[email protected]': '@msgpackr-extract/[email protected]':
resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==}
cpu: [arm64] cpu: [arm64]
@@ -541,6 +566,100 @@ packages:
'@standard-schema/[email protected]': '@standard-schema/[email protected]':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==}
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [android]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [darwin]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==}
engines: {node: '>= 20'}
cpu: [x64]
os: [darwin]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==}
engines: {node: '>= 20'}
cpu: [x64]
os: [freebsd]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==}
engines: {node: '>= 20'}
cpu: [arm]
os: [linux]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
- '@napi-rs/wasm-runtime'
- '@emnapi/core'
- '@emnapi/runtime'
- '@tybys/wasm-util'
- '@emnapi/wasi-threads'
- tslib
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [win32]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==}
engines: {node: '>= 20'}
cpu: [x64]
os: [win32]
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==}
engines: {node: '>= 20'}
'@tailwindcss/[email protected]':
resolution: {integrity: sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==}
peerDependencies:
vite: ^5.2.0 || ^6 || ^7 || ^8
'@testing-library/[email protected]': '@testing-library/[email protected]':
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -804,6 +923,10 @@ packages:
[email protected]: [email protected]:
resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
[email protected]:
resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==}
engines: {node: '>=10.13.0'}
[email protected]: [email protected]:
resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
@@ -870,6 +993,9 @@ packages:
resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
engines: {node: '>=18'} engines: {node: '>=18'}
[email protected]:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
[email protected]: [email protected]:
resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==}
engines: {node: '>=16.9.0'} engines: {node: '>=16.9.0'}
@@ -885,6 +1011,10 @@ packages:
[email protected]: [email protected]:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
[email protected]:
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
[email protected]: [email protected]:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -1151,6 +1281,13 @@ packages:
[email protected]: [email protected]:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
[email protected]:
resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==}
[email protected]:
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
engines: {node: '>=6'}
[email protected]: [email protected]:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
@@ -1538,6 +1675,8 @@ snapshots:
'@exodus/[email protected]': {} '@exodus/[email protected]': {}
'@fontsource-variable/[email protected]': {}
'@fontsource-variable/[email protected]': {} '@fontsource-variable/[email protected]': {}
'@fontsource/[email protected]': {} '@fontsource/[email protected]': {}
@@ -1548,8 +1687,25 @@ snapshots:
dependencies: dependencies:
hono: 4.12.27 hono: 4.12.27
'@jridgewell/[email protected]':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/[email protected]':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
'@jridgewell/[email protected]': {}
'@jridgewell/[email protected]': {} '@jridgewell/[email protected]': {}
'@jridgewell/[email protected]':
dependencies:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@msgpackr-extract/[email protected]': '@msgpackr-extract/[email protected]':
optional: true optional: true
@@ -1630,6 +1786,74 @@ snapshots:
'@standard-schema/[email protected]': {} '@standard-schema/[email protected]': {}
'@tailwindcss/[email protected]':
dependencies:
'@jridgewell/remapping': 2.3.5
enhanced-resolve: 5.21.6
jiti: 2.7.0
lightningcss: 1.32.0
magic-string: 0.30.21
source-map-js: 1.2.1
tailwindcss: 4.3.2
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optional: true
'@tailwindcss/[email protected]':
optionalDependencies:
'@tailwindcss/oxide-android-arm64': 4.3.2
'@tailwindcss/oxide-darwin-arm64': 4.3.2
'@tailwindcss/oxide-darwin-x64': 4.3.2
'@tailwindcss/oxide-freebsd-x64': 4.3.2
'@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2
'@tailwindcss/oxide-linux-arm64-gnu': 4.3.2
'@tailwindcss/oxide-linux-arm64-musl': 4.3.2
'@tailwindcss/oxide-linux-x64-gnu': 4.3.2
'@tailwindcss/oxide-linux-x64-musl': 4.3.2
'@tailwindcss/oxide-wasm32-wasi': 4.3.2
'@tailwindcss/oxide-win32-arm64-msvc': 4.3.2
'@tailwindcss/oxide-win32-x64-msvc': 4.3.2
'@tailwindcss/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected]))':
dependencies:
'@tailwindcss/node': 4.3.2
'@tailwindcss/oxide': 4.3.2
tailwindcss: 4.3.2
vite: 8.1.2(@types/[email protected])([email protected])([email protected])([email protected])
'@testing-library/[email protected]': '@testing-library/[email protected]':
dependencies: dependencies:
'@babel/code-frame': 7.29.7 '@babel/code-frame': 7.29.7
@@ -1713,10 +1937,10 @@ snapshots:
dependencies: dependencies:
csstype: 3.2.3 csstype: 3.2.3
'@vitejs/[email protected]([email protected](@types/[email protected])([email protected])([email protected]))': '@vitejs/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected]))':
dependencies: dependencies:
'@rolldown/pluginutils': 1.0.1 '@rolldown/pluginutils': 1.0.1
vite: 8.1.2(@types/[email protected])([email protected])([email protected]) vite: 8.1.2(@types/[email protected])([email protected])([email protected])([email protected])
'@vitest/[email protected]': '@vitest/[email protected]':
dependencies: dependencies:
@@ -1727,13 +1951,13 @@ snapshots:
chai: 6.2.2 chai: 6.2.2
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
'@vitest/[email protected]([email protected](@types/[email protected])([email protected])([email protected]))': '@vitest/[email protected]([email protected](@types/[email protected])([email protected])([email protected])([email protected]))':
dependencies: dependencies:
'@vitest/spy': 4.1.9 '@vitest/spy': 4.1.9
estree-walker: 3.0.3 estree-walker: 3.0.3
magic-string: 0.30.21 magic-string: 0.30.21
optionalDependencies: optionalDependencies:
vite: 8.1.2(@types/[email protected])([email protected])([email protected]) vite: 8.1.2(@types/[email protected])([email protected])([email protected])([email protected])
'@vitest/[email protected]': '@vitest/[email protected]':
dependencies: dependencies:
@@ -1898,6 +2122,11 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.3
[email protected]: {} [email protected]: {}
[email protected]: {} [email protected]: {}
@@ -1965,6 +2194,8 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]: {}
[email protected]: {} [email protected]: {}
[email protected]: [email protected]:
@@ -1977,6 +2208,8 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]: {}
[email protected]: {} [email protected]: {}
[email protected]: [email protected]:
@@ -2224,6 +2457,10 @@ snapshots:
[email protected]: {} [email protected]: {}
[email protected]: {}
[email protected]: {}
[email protected]: {} [email protected]: {}
[email protected]: {} [email protected]: {}
@@ -2273,7 +2510,7 @@ snapshots:
optionalDependencies: optionalDependencies:
typescript: 6.0.3 typescript: 6.0.3
[email protected](@types/[email protected])([email protected])([email protected]): [email protected](@types/[email protected])([email protected])([email protected])([email protected]):
dependencies: dependencies:
lightningcss: 1.32.0 lightningcss: 1.32.0
picomatch: 4.0.4 picomatch: 4.0.4
@@ -2284,12 +2521,13 @@ snapshots:
'@types/node': 26.1.0 '@types/node': 26.1.0
esbuild: 0.28.1 esbuild: 0.28.1
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.7.0
tsx: 4.22.4 tsx: 4.22.4
[email protected](@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])): [email protected](@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])):
dependencies: dependencies:
'@vitest/expect': 4.1.9 '@vitest/expect': 4.1.9
'@vitest/mocker': 4.1.9([email protected](@types/[email protected])([email protected])([email protected])) '@vitest/mocker': 4.1.9([email protected](@types/[email protected])([email protected])([email protected])([email protected]))
'@vitest/pretty-format': 4.1.9 '@vitest/pretty-format': 4.1.9
'@vitest/runner': 4.1.9 '@vitest/runner': 4.1.9
'@vitest/snapshot': 4.1.9 '@vitest/snapshot': 4.1.9
@@ -2306,7 +2544,7 @@ snapshots:
tinyexec: 1.2.4 tinyexec: 1.2.4
tinyglobby: 0.2.17 tinyglobby: 0.2.17
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
vite: 8.1.2(@types/[email protected])([email protected])([email protected]) vite: 8.1.2(@types/[email protected])([email protected])([email protected])([email protected])
why-is-node-running: 2.3.0 why-is-node-running: 2.3.0
optionalDependencies: optionalDependencies:
'@types/node': 26.1.0 '@types/node': 26.1.0