diff --git a/core/epistemic_state.py b/core/epistemic_state.py index f40b9566..751bfdb8 100644 --- a/core/epistemic_state.py +++ b/core/epistemic_state.py @@ -9,7 +9,17 @@ serialize stably into JSONL, metadata dictionaries, and test fixtures. from __future__ import annotations from enum import Enum, unique -from typing import Any +from typing import Any, Literal + + +GroundingSource = Literal["pack", "teaching", "vault", "partial", "oov", "none"] +"""Ratified grounding-source labels. + +Single source of truth for the values ``epistemic_state_for_grounding_source`` +maps, the cold-start-grounding lane validates, and the Workbench UI badges +bind to (ADR-0162 ยง3d). Adding a value here without adding a corresponding +badge fails the build-time enum coverage test under ``workbench-ui/``. +""" @unique @@ -136,6 +146,7 @@ def epistemic_state_for_grounding_source(source: str | None) -> EpistemicState: __all__ = [ "EpistemicState", + "GroundingSource", "NormativeClearance", "clearance_from_verdicts", "coerce_epistemic_state", diff --git a/docs/workbench/design-system.md b/docs/workbench/design-system.md new file mode 100644 index 00000000..a7054c13 --- /dev/null +++ b/docs/workbench/design-system.md @@ -0,0 +1,61 @@ +# Workbench Design System v1 + +This document records the Branch 1 substrate for ADR-0162, cross-referenced with +[ADR-0160](../decisions/ADR-0160-core-workbench-v1.md) and +[ADR-0161](../decisions/ADR-0161-hitl-async-queue.md). + +The implementation lives in `workbench-ui/`. The preview page is `/preview`: + +```bash +cd workbench-ui && pnpm install && pnpm preview +``` + +Branch 1 is intentionally static and read-only. It adds no backend, no API +client, no route shell beyond `/preview`, and no runtime mutation surface. + +## Token Substrate + +Tokens are defined in `workbench-ui/src/design/tokens/tokens.css` and mirrored +to typed TypeScript by `workbench-ui/scripts/generate-tokens.ts`. The build +prehook regenerates `tokens.ts`; the token test fails if CSS and TypeScript +diverge. + +The theme is dark-only. Fonts are self-hosted from `workbench-ui/public/fonts/` +and referenced through local `@font-face` rules only. + +## Badge Tables + +Badge components live under `workbench-ui/src/design/components/badges/`. +Each component accepts a typed enum value, never an arbitrary string. + +| Badge | Values | Source | +|---|---:|---| +| `EpistemicStateBadge` | 15 | `core/epistemic_state.py` | +| `NormativeClearanceBadge` | 4 | `core/epistemic_state.py` | +| `ReviewStateBadge` | 4 | `teaching/proposals.py` | +| `GroundingSourceBadge` | 6 | ADR-0162 Branch 1 contract | +| `TraceHashBadge` | copyable digest | ADR-0153 / ADR-0160 | + +`scripts/dump-enums.py` performs a read-only AST walk over the Python enum +sources. `pnpm test:enum-coverage` regenerates `workbench-ui/enum-snapshot.json` +and asserts exact UI coverage so engine enum drift fails loudly at build/test +time. + +## JSON Viewer + +`StableJsonViewer` preserves raw source spans for strings and numbers, renders +object keys in deterministic lexicographic order, copies JSON Pointer paths, +renders a source-byte SHA-256 badge, supports side-by-side leaf diffs, and +refuses inline rendering above 16 MiB. + +## Motion And Keyboard + +All motion uses tokenized durations/easing. `prefers-reduced-motion: reduce` +collapses tokenized durations to instant. + +The preview page installs the Branch 1 keyboard baseline: + +- `Cmd+K` / `Ctrl+K` opens the stub command palette. +- `Esc` closes overlays. +- Interactive primitives use `--color-focus-ring`. +- Preview tab order follows DOM order. diff --git a/scripts/dump-enums.py b/scripts/dump-enums.py new file mode 100644 index 00000000..f0dda58c --- /dev/null +++ b/scripts/dump-enums.py @@ -0,0 +1,60 @@ +"""Dump ratified engine enum values for Workbench UI coverage tests. + +Read-only helper: parses source with Python AST and writes JSON to stdout. +""" + +from __future__ import annotations + +import ast +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def enum_values(path: Path, class_name: str) -> list[str]: + module = ast.parse(path.read_text(encoding="utf-8")) + for node in module.body: + if isinstance(node, ast.ClassDef) and node.name == class_name: + values: list[str] = [] + for statement in node.body: + if ( + isinstance(statement, ast.Assign) + and isinstance(statement.value, ast.Constant) + and isinstance(statement.value.value, str) + ): + values.append(statement.value.value) + return values + raise SystemExit(f"enum class not found: {class_name}") + + +def literal_values(path: Path, name: str) -> list[str]: + module = ast.parse(path.read_text(encoding="utf-8")) + for node in module.body: + if isinstance(node, ast.Assign): + if not any(isinstance(target, ast.Name) and target.id == name for target in node.targets): + continue + value = node.value + if ( + isinstance(value, ast.Subscript) + and isinstance(value.value, ast.Name) + and value.value.id == "Literal" + ): + items = value.slice.elts if isinstance(value.slice, ast.Tuple) else [value.slice] + return [ + item.value + for item in items + if isinstance(item, ast.Constant) and isinstance(item.value, str) + ] + raise SystemExit(f"literal alias not found: {name}") + + +snapshot = { + "EpistemicState": enum_values(ROOT / "core" / "epistemic_state.py", "EpistemicState"), + "GroundingSource": literal_values(ROOT / "core" / "epistemic_state.py", "GroundingSource"), + "NormativeClearance": enum_values(ROOT / "core" / "epistemic_state.py", "NormativeClearance"), + "ReviewState": literal_values(ROOT / "teaching" / "proposals.py", "ReviewState"), +} + +print(json.dumps(snapshot, indent=2, sort_keys=True)) diff --git a/workbench-ui/.gitignore b/workbench-ui/.gitignore new file mode 100644 index 00000000..4812e78d --- /dev/null +++ b/workbench-ui/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +dist/ +*.tsbuildinfo +vite.config.js +vite.config.d.ts +scripts/*.js +scripts/*.d.ts diff --git a/workbench-ui/enum-snapshot.json b/workbench-ui/enum-snapshot.json new file mode 100644 index 00000000..7d2e04a9 --- /dev/null +++ b/workbench-ui/enum-snapshot.json @@ -0,0 +1,39 @@ +{ + "EpistemicState": [ + "perceived", + "evidenced", + "evidenced_incomplete", + "verified", + "decoded", + "decoded_unarticulated", + "inferred", + "unverified_possible", + "unverified_novel", + "contradicted", + "ambiguous", + "undetermined", + "scope_boundary", + "computationally_bounded", + "epistemic_state_needed" + ], + "GroundingSource": [ + "pack", + "teaching", + "vault", + "partial", + "oov", + "none" + ], + "NormativeClearance": [ + "cleared", + "violated", + "unassessable", + "suppressed" + ], + "ReviewState": [ + "pending", + "accepted", + "rejected", + "withdrawn" + ] +} diff --git a/workbench-ui/index.html b/workbench-ui/index.html new file mode 100644 index 00000000..1b408b32 --- /dev/null +++ b/workbench-ui/index.html @@ -0,0 +1,12 @@ + + + + + + CORE Workbench Preview + + +
+ + + diff --git a/workbench-ui/package.json b/workbench-ui/package.json new file mode 100644 index 00000000..11b43eb7 --- /dev/null +++ b/workbench-ui/package.json @@ -0,0 +1,45 @@ +{ + "name": "core-workbench-ui", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "prebuild": "pnpm generate:tokens", + "build": "tsc -b && vite build", + "dev": "vite --host 127.0.0.1", + "preview": "vite preview --host 127.0.0.1", + "test": "vitest run", + "test:enum-coverage": "pnpm enum:snapshot && vitest run src/design/components/badges/enumCoverage.test.ts", + "generate:tokens": "tsx scripts/generate-tokens.ts", + "enum:snapshot": "cd .. && uv run python scripts/dump-enums.py > workbench-ui/enum-snapshot.json" + }, + "dependencies": { + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-popover": "1.1.15", + "@radix-ui/react-slot": "1.2.3", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "lucide-react": "0.468.0", + "react": "18.3.1", + "react-dom": "18.3.1", + "tailwind-merge": "2.6.0" + }, + "devDependencies": { + "@testing-library/jest-dom": "6.6.3", + "@testing-library/react": "16.1.0", + "@testing-library/user-event": "14.5.2", + "@types/node": "22.10.2", + "@types/react": "18.3.17", + "@types/react-dom": "18.3.5", + "@vitejs/plugin-react": "4.3.4", + "autoprefixer": "10.4.20", + "happy-dom": "15.11.7", + "postcss": "8.4.49", + "tailwindcss": "3.4.17", + "tsx": "4.19.2", + "typescript": "5.7.2", + "vite": "5.4.11", + "vitest": "2.1.8" + }, + "packageManager": "pnpm@9.15.0" +} diff --git a/workbench-ui/pnpm-lock.yaml b/workbench-ui/pnpm-lock.yaml new file mode 100644 index 00000000..2acb0a13 --- /dev/null +++ b/workbench-ui/pnpm-lock.yaml @@ -0,0 +1,3113 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@radix-ui/react-dialog': + specifier: 1.1.15 + version: 1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': + specifier: 1.1.15 + version: 1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': + specifier: 1.2.3 + version: 1.2.3(@types/react@18.3.17)(react@18.3.1) + class-variance-authority: + specifier: 0.7.1 + version: 0.7.1 + clsx: + specifier: 2.1.1 + version: 2.1.1 + lucide-react: + specifier: 0.468.0 + version: 0.468.0(react@18.3.1) + react: + specifier: 18.3.1 + version: 18.3.1 + react-dom: + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) + tailwind-merge: + specifier: 2.6.0 + version: 2.6.0 + devDependencies: + '@testing-library/jest-dom': + specifier: 6.6.3 + version: 6.6.3 + '@testing-library/react': + specifier: 16.1.0 + version: 16.1.0(@testing-library/dom@10.4.1)(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@testing-library/user-event': + specifier: 14.5.2 + version: 14.5.2(@testing-library/dom@10.4.1) + '@types/node': + specifier: 22.10.2 + version: 22.10.2 + '@types/react': + specifier: 18.3.17 + version: 18.3.17 + '@types/react-dom': + specifier: 18.3.5 + version: 18.3.5(@types/react@18.3.17) + '@vitejs/plugin-react': + specifier: 4.3.4 + version: 4.3.4(vite@5.4.11(@types/node@22.10.2)) + autoprefixer: + specifier: 10.4.20 + version: 10.4.20(postcss@8.4.49) + happy-dom: + specifier: 15.11.7 + version: 15.11.7 + postcss: + specifier: 8.4.49 + version: 8.4.49 + tailwindcss: + specifier: 3.4.17 + version: 3.4.17 + tsx: + specifier: 4.19.2 + version: 4.19.2 + typescript: + specifier: 5.7.2 + version: 5.7.2 + vite: + specifier: 5.4.11 + version: 5.4.11(@types/node@22.10.2) + vitest: + specifier: 2.1.8 + version: 2.1.8(@types/node@22.10.2)(happy-dom@15.11.7) + +packages: + + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + cpu: [x64] + os: [win32] + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.6.3': + resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.1.0': + resolution: {integrity: sha512-Q2ToPvg0KsVL0ohND9A3zLJWcOXXcO8IDu3fj11KhNt0UlCWyFyvnCIBkd12tidB2lkiVRG8VFqdhcqhqnAQtg==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.5.2': + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@22.10.2': + resolution: {integrity: sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.5': + resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.17': + resolution: {integrity: sha512-opAQ5no6LqJNo9TqnxBKsgnkIYHozW9KSTlFVoSUJYh1Fl/sswkEoqIugRSm7tbh6pABtYjGAjW+GOS23j8qbw==} + + '@vitejs/plugin-react@4.3.4': + resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + + '@vitest/expect@2.1.8': + resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + + '@vitest/mocker@2.1.8': + resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.8': + resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} + + '@vitest/runner@2.1.8': + resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + + '@vitest/snapshot@2.1.8': + resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + + '@vitest/spy@2.1.8': + resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + + '@vitest/utils@2.1.8': + resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + baseline-browser-mapping@2.10.32: + resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} + engines: {node: '>=6.0.0'} + hasBin: true + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + electron-to-chromium@1.5.361: + resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + happy-dom@15.11.7: + resolution: {integrity: sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==} + engines: {node: '>=18.0.0'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + jiti@1.21.7: + resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.468.0: + resolution: {integrity: sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwind-merge@2.6.0: + resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + + tailwindcss@3.4.17: + resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + engines: {node: '>=14.0.0'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.19.2: + resolution: {integrity: sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vite-node@2.1.8: + resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@2.1.8: + resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.8 + '@vitest/ui': 2.1.8 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + +snapshots: + + '@adobe/css-tools@4.5.0': {} + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/utils@0.2.11': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-context@1.1.2(@types/react@18.3.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.17)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.17)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-id@1.1.1(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-popover@1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.17)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.17)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@radix-ui/react-slot@1.2.3(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.17)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + + '@radix-ui/rect@1.1.1': {} + + '@rollup/rollup-android-arm-eabi@4.60.4': + optional: true + + '@rollup/rollup-android-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.4': + optional: true + + '@rollup/rollup-darwin-x64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.4': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.6.3': + dependencies: + '@adobe/css-tools': 4.5.0 + aria-query: 5.3.2 + chalk: 3.0.0 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + lodash: 4.18.1 + redent: 3.0.0 + + '@testing-library/react@16.1.0(@testing-library/dom@10.4.1)(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + + '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/node@22.10.2': + dependencies: + undici-types: 6.20.0 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.5(@types/react@18.3.17)': + dependencies: + '@types/react': 18.3.17 + + '@types/react@18.3.17': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@vitejs/plugin-react@4.3.4(vite@5.4.11(@types/node@22.10.2))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 5.4.11(@types/node@22.10.2) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@2.1.8': + dependencies: + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.3.3 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.8(vite@5.4.11(@types/node@22.10.2))': + dependencies: + '@vitest/spy': 2.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 5.4.11(@types/node@22.10.2) + + '@vitest/pretty-format@2.1.8': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/pretty-format@2.1.9': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.8': + dependencies: + '@vitest/utils': 2.1.8 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + magic-string: 0.30.21 + pathe: 1.1.2 + + '@vitest/spy@2.1.8': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + loupe: 3.2.1 + tinyrainbow: 1.2.0 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + assertion-error@2.0.1: {} + + autoprefixer@10.4.20(postcss@8.4.49): + dependencies: + browserslist: 4.28.2 + caniuse-lite: 1.0.30001793 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.4.49 + postcss-value-parser: 4.2.0 + + baseline-browser-mapping@2.10.32: {} + + binary-extensions@2.3.0: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.32 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.361 + node-releases: 2.0.46 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + cac@6.7.14: {} + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001793: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@3.0.0: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@2.1.3: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@4.1.1: {} + + convert-source-map@2.0.0: {} + + css.escape@1.5.1: {} + + cssesc@3.0.0: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + dequal@2.0.3: {} + + detect-node-es@1.1.0: {} + + didyoumean@1.2.2: {} + + dlv@1.1.3: {} + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + electron-to-chromium@1.5.361: {} + + entities@4.5.0: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + escalade@3.2.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.3.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + fraction.js@4.3.7: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-nonce@1.0.1: {} + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + happy-dom@15.11.7: + dependencies: + entities: 4.5.0 + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + + has-flag@4.0.0: {} + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + indent-string@4.0.0: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.3 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + jiti@1.21.7: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + lodash@4.18.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.468.0(react@18.3.1): + dependencies: + react: 18.3.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + min-indent@1.0.1: {} + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.12: {} + + node-releases@2.0.46: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + path-parse@1.0.7: {} + + pathe@1.1.2: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + pify@2.3.0: {} + + pirates@4.0.7: {} + + postcss-import@15.1.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.12 + + postcss-js@4.1.0(postcss@8.4.49): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.49 + + postcss-load-config@4.0.2(postcss@8.4.49): + dependencies: + lilconfig: 3.1.3 + yaml: 2.9.0 + optionalDependencies: + postcss: 8.4.49 + + postcss-nested@6.2.0(postcss@8.4.49): + dependencies: + postcss: 8.4.49 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.49: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + queue-microtask@1.2.3: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@17.0.2: {} + + react-refresh@0.14.2: {} + + react-remove-scroll-bar@2.3.8(@types/react@18.3.17)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.3.17)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.17 + + react-remove-scroll@2.7.2(@types/react@18.3.17)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.17)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.17)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.3.17)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.17)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + + react-style-singleton@2.2.3(@types/react@18.3.17)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.17 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.12: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rollup@4.60.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.16 + ts-interface-checker: 0.1.13 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwind-merge@2.6.0: {} + + tailwindcss@3.4.17: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.7 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.4.49 + postcss-import: 15.1.0(postcss@8.4.49) + postcss-js: 4.1.0(postcss@8.4.49) + postcss-load-config: 4.0.2(postcss@8.4.49) + postcss-nested: 6.2.0(postcss@8.4.49) + postcss-selector-parser: 6.1.2 + resolve: 1.22.12 + sucrase: 3.35.1 + transitivePeerDependencies: + - ts-node + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinypool@1.1.1: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-interface-checker@0.1.13: {} + + tslib@2.8.1: {} + + tsx@4.19.2: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.14.0 + optionalDependencies: + fsevents: 2.3.3 + + typescript@5.7.2: {} + + undici-types@6.20.0: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-callback-ref@1.3.3(@types/react@18.3.17)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.17 + + use-sidecar@1.1.3(@types/react@18.3.17)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.17 + + util-deprecate@1.0.2: {} + + vite-node@2.1.8(@types/node@22.10.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 1.1.2 + vite: 5.4.11(@types/node@22.10.2) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.11(@types/node@22.10.2): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.49 + rollup: 4.60.4 + optionalDependencies: + '@types/node': 22.10.2 + fsevents: 2.3.3 + + vitest@2.1.8(@types/node@22.10.2)(happy-dom@15.11.7): + dependencies: + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.2)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.3.0 + magic-string: 0.30.21 + pathe: 1.1.2 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.1.1 + tinyrainbow: 1.2.0 + vite: 5.4.11(@types/node@22.10.2) + vite-node: 2.1.8(@types/node@22.10.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.10.2 + happy-dom: 15.11.7 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + webidl-conversions@7.0.0: {} + + whatwg-mimetype@3.0.0: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + yallist@3.1.1: {} + + yaml@2.9.0: {} diff --git a/workbench-ui/postcss.config.js b/workbench-ui/postcss.config.js new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ b/workbench-ui/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/workbench-ui/preview/README.md b/workbench-ui/preview/README.md new file mode 100644 index 00000000..1cf46de6 --- /dev/null +++ b/workbench-ui/preview/README.md @@ -0,0 +1,8 @@ +# Preview + +The Vite route at `/preview` renders the Branch 1 design substrate. + +```bash +pnpm build +pnpm preview +``` diff --git a/workbench-ui/preview/preview.png b/workbench-ui/preview/preview.png new file mode 100644 index 00000000..6c57c90e Binary files /dev/null and b/workbench-ui/preview/preview.png differ diff --git a/workbench-ui/public/fonts/Inter-Regular.woff2 b/workbench-ui/public/fonts/Inter-Regular.woff2 new file mode 100644 index 00000000..5a8d3e72 Binary files /dev/null and b/workbench-ui/public/fonts/Inter-Regular.woff2 differ diff --git a/workbench-ui/public/fonts/JetBrainsMono-Regular.woff2 b/workbench-ui/public/fonts/JetBrainsMono-Regular.woff2 new file mode 100644 index 00000000..66c54672 Binary files /dev/null and b/workbench-ui/public/fonts/JetBrainsMono-Regular.woff2 differ diff --git a/workbench-ui/scripts/generate-tokens.ts b/workbench-ui/scripts/generate-tokens.ts new file mode 100644 index 00000000..a07b38be --- /dev/null +++ b/workbench-ui/scripts/generate-tokens.ts @@ -0,0 +1,22 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const cssPath = resolve(here, "../src/design/tokens/tokens.css"); +const outPath = resolve(here, "../src/design/tokens/tokens.ts"); + +const css = readFileSync(cssPath, "utf8"); +const tokenMatches = [...css.matchAll(/--([a-z0-9-]+):\s*([^;]+);/g)]; +const tokens = Object.fromEntries( + tokenMatches.map(([, name, value]) => [name, value.trim()]), +); + +const body = `// Generated by scripts/generate-tokens.ts. Do not edit by hand. +export const tokens = ${JSON.stringify(tokens, null, 2)} as const; + +export type DesignTokenName = keyof typeof tokens; +export type DesignTokenValue = (typeof tokens)[DesignTokenName]; +`; + +writeFileSync(outPath, body); diff --git a/workbench-ui/src/design/components/StableJsonViewer/StableJsonViewer.test.tsx b/workbench-ui/src/design/components/StableJsonViewer/StableJsonViewer.test.tsx new file mode 100644 index 00000000..961d1289 --- /dev/null +++ b/workbench-ui/src/design/components/StableJsonViewer/StableJsonViewer.test.tsx @@ -0,0 +1,88 @@ +import { render, screen, waitFor, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it } from "vitest"; +import { StableJsonViewer } from "./StableJsonViewer"; +import { diffLeaves, leaves, parseJsonSource } from "./jsonModel"; + +async function sha256Hex(source: string): Promise { + const bytes = new TextEncoder().encode(source); + const hash = await crypto.subtle.digest("SHA-256", bytes); + return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join(""); +} + +describe("StableJsonViewer invariants", () => { + it("renders object keys in deterministic lexicographic order", () => { + render(); + const rows = within(screen.getByTestId("json-rows")).getAllByRole("button"); + expect(rows.map((row) => row.textContent)).toEqual(["/a 2", "/z 1"]); + }); + + it("preserves source string bytes without smart quotes or entity coercion", () => { + const source = "{\"quote\":\"a \\\"raw\\\" & \"}"; + render(); + expect(screen.getByText(/"a \\"raw\\" & "/)).toBeInTheDocument(); + }); + + it("preserves numeric notation and int/float distinction", () => { + render(); + expect(screen.getByText((_, node) => node?.textContent === "/epsilon 1e-6")).toBeInTheDocument(); + expect(screen.getByText((_, node) => node?.textContent === "/int 1")).toBeInTheDocument(); + expect(screen.getByText((_, node) => node?.textContent === "/float 1.0")).toBeInTheDocument(); + }); + + it("copy-path returns a JSON Pointer", async () => { + const user = userEvent.setup(); + const writeText = vi.fn().mockResolvedValue(undefined); + Object.defineProperty(navigator, "clipboard", { + configurable: true, + value: { writeText }, + }); + render(); + await user.click(screen.getByText(/\/scenes\/3\/detail\/object/)); + expect(writeText).toHaveBeenCalledWith("/scenes/3/detail/object"); + }); + + it("diff mode is side-by-side and marks only changed leaves with glyphs", () => { + render(); + expect(screen.getByTestId("json-diff")).toBeInTheDocument(); + expect(screen.getAllByLabelText("changed")).toHaveLength(2); + expect(screen.getAllByLabelText("added")).toHaveLength(2); + expect(screen.getAllByLabelText("same")).toHaveLength(2); + + const modelDiff = diffLeaves(leaves(parseJsonSource('{"a":1}')), leaves(parseJsonSource('{"a":2}'))); + expect(modelDiff).toMatchObject([{ pointer: "/a", kind: "changed" }]); + }); + + it("virtualizes large documents and refuses inline render above 16 MiB", () => { + const manyLeaves = `{"items":[${Array.from({ length: 1001 }, (_, i) => i).join(",")}]}`; + const { rerender } = render(); + expect(screen.getByTestId("virtualized-json")).toHaveTextContent("virtualized 1000 of 1001 leaves"); + + rerender(); + expect(screen.getByText(/larger than 16 MiB/)).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /Open in external viewer/ })).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /Copy path/ })).toBeInTheDocument(); + }); + + it("renders a sha256 digest badge over the source bytes and copies the full digest on click", async () => { + const user = userEvent.setup(); + const writeText = vi.fn().mockResolvedValue(undefined); + Object.defineProperty(navigator, "clipboard", { + configurable: true, + value: { writeText }, + }); + const source = '{"a":1}'; + const expectedDigest = await sha256Hex(source); + + render(); + + const digestButton = await screen.findByRole("button", { name: /sha256:/ }); + // The badge displays the truncated prefix. + await waitFor(() => { + expect(digestButton.textContent).toBe(`sha256:${expectedDigest.slice(0, 12)}`); + }); + // Clicking the badge copies the FULL digest, not the truncated prefix. + await user.click(digestButton); + expect(writeText).toHaveBeenCalledWith(expectedDigest); + }); +}); diff --git a/workbench-ui/src/design/components/StableJsonViewer/StableJsonViewer.tsx b/workbench-ui/src/design/components/StableJsonViewer/StableJsonViewer.tsx new file mode 100644 index 00000000..a81102de --- /dev/null +++ b/workbench-ui/src/design/components/StableJsonViewer/StableJsonViewer.tsx @@ -0,0 +1,136 @@ +import { Copy, ExternalLink } from "lucide-react"; +import { useEffect, useMemo, useState } from "react"; +import { Button } from "../primitives/Button"; +import { copyText, cn } from "../../lib"; +import { countLeaves, diffLeaves, leaves, parseJsonSource, type DiffKind, type JsonNode } from "./jsonModel"; + +const MAX_INLINE_BYTES = 16 * 1024 * 1024; +const VIRTUALIZE_LEAVES = 1000; + +function bytes(source: string) { + return new TextEncoder().encode(source); +} + +async function sha256(source: string) { + const hash = await crypto.subtle.digest("SHA-256", bytes(source)); + return [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join(""); +} + +function nodeRows(node: JsonNode, pointer = "", depth = 0): { pointer: string; depth: number; label: string; raw?: string }[] { + if (node.kind === "literal") return [{ pointer, depth, label: pointer.split("/").at(-1) || "/", raw: node.raw }]; + if (node.kind === "array") { + return node.items.flatMap((item, index) => nodeRows(item, `${pointer}/${index}`, depth + 1)); + } + return node.entries.flatMap((entry) => nodeRows(entry.value, `${pointer}/${entry.key.replaceAll("~", "~0").replaceAll("/", "~1")}`, depth + 1)); +} + +function DigestBadge({ source }: { source: string }) { + const [digest, setDigest] = useState(""); + useEffect(() => { + void sha256(source).then(setDigest); + }, [source]); + return ( + + ); +} + +function ChangeGlyph({ kind }: { kind: DiffKind }) { + const glyph = kind === "added" ? "+" : kind === "removed" ? "-" : kind === "changed" ? "~" : " "; + return {glyph}; +} + +export function StableJsonViewer({ + source, + compareSource, +}: { + source: string; + compareSource?: string; +}) { + const rawBytes = bytes(source).byteLength; + const parsed = useMemo(() => (rawBytes <= MAX_INLINE_BYTES ? parseJsonSource(source) : null), [rawBytes, source]); + const leafCount = parsed ? countLeaves(parsed) : 0; + + if (rawBytes > MAX_INLINE_BYTES) { + return ( +
+
+ +
+

JSON is larger than 16 MiB and was not rendered inline.

+ + +
+ ); + } + + if (compareSource) { + const right = parseJsonSource(compareSource); + const rows = diffLeaves(leaves(parsed!), leaves(right)); + return ( +
+
+ + diff mode +
+
+ {rows.map((row) => ( +
+ + +
+ ))} +
+
+ ); + } + + const rows = nodeRows(parsed!); + const visibleRows = leafCount > VIRTUALIZE_LEAVES ? rows.slice(0, VIRTUALIZE_LEAVES) : rows; + return ( +
+
+ + {leafCount > VIRTUALIZE_LEAVES ? virtualized {VIRTUALIZE_LEAVES} of {leafCount} leaves : null} +
+
+ {visibleRows.map((row) => ( + + ))} +
+
+ ); +} diff --git a/workbench-ui/src/design/components/StableJsonViewer/index.ts b/workbench-ui/src/design/components/StableJsonViewer/index.ts new file mode 100644 index 00000000..eaabb8c7 --- /dev/null +++ b/workbench-ui/src/design/components/StableJsonViewer/index.ts @@ -0,0 +1,2 @@ +export { StableJsonViewer } from "./StableJsonViewer"; +export { parseJsonSource, leaves, diffLeaves } from "./jsonModel"; diff --git a/workbench-ui/src/design/components/StableJsonViewer/jsonModel.ts b/workbench-ui/src/design/components/StableJsonViewer/jsonModel.ts new file mode 100644 index 00000000..bdcba06e --- /dev/null +++ b/workbench-ui/src/design/components/StableJsonViewer/jsonModel.ts @@ -0,0 +1,155 @@ +export type JsonNode = + | { kind: "object"; entries: { key: string; keyRaw: string; value: JsonNode }[] } + | { kind: "array"; items: JsonNode[] } + | { kind: "literal"; raw: string; valueKind: "string" | "number" | "boolean" | "null" }; + +export type Leaf = { pointer: string; raw: string; valueKind: string }; + +class Parser { + private index = 0; + + constructor(private readonly source: string) {} + + parse(): JsonNode { + this.ws(); + const node = this.value(); + this.ws(); + if (this.index !== this.source.length) throw new Error("Unexpected trailing JSON content."); + return node; + } + + private value(): JsonNode { + this.ws(); + const ch = this.source[this.index]; + if (ch === "{") return this.object(); + if (ch === "[") return this.array(); + if (ch === '"') return { kind: "literal", raw: this.stringRaw(), valueKind: "string" }; + if (ch === "-" || /\d/.test(ch)) return { kind: "literal", raw: this.numberRaw(), valueKind: "number" }; + for (const word of ["true", "false", "null"] as const) { + if (this.source.startsWith(word, this.index)) { + this.index += word.length; + return { kind: "literal", raw: word, valueKind: word === "null" ? "null" : "boolean" }; + } + } + throw new Error(`Unexpected JSON token at ${this.index}.`); + } + + private object(): JsonNode { + this.index++; + this.ws(); + const entries: { key: string; keyRaw: string; value: JsonNode }[] = []; + if (this.source[this.index] === "}") { + this.index++; + return { kind: "object", entries }; + } + while (this.index < this.source.length) { + const keyRaw = this.stringRaw(); + const key = JSON.parse(keyRaw) as string; + this.ws(); + this.expect(":"); + const value = this.value(); + entries.push({ key, keyRaw, value }); + this.ws(); + if (this.source[this.index] === "}") { + this.index++; + break; + } + this.expect(","); + this.ws(); + } + entries.sort((a, b) => a.key.localeCompare(b.key)); + return { kind: "object", entries }; + } + + private array(): JsonNode { + this.index++; + this.ws(); + const items: JsonNode[] = []; + if (this.source[this.index] === "]") { + this.index++; + return { kind: "array", items }; + } + while (this.index < this.source.length) { + items.push(this.value()); + this.ws(); + if (this.source[this.index] === "]") { + this.index++; + break; + } + this.expect(","); + this.ws(); + } + return { kind: "array", items }; + } + + private stringRaw(): string { + const start = this.index; + this.expect('"'); + while (this.index < this.source.length) { + const ch = this.source[this.index++]; + if (ch === "\\") { + this.index++; + } else if (ch === '"') { + return this.source.slice(start, this.index); + } + } + throw new Error("Unterminated string."); + } + + private numberRaw(): string { + const start = this.index; + if (this.source[this.index] === "-") this.index++; + while (/\d/.test(this.source[this.index] ?? "")) this.index++; + if (this.source[this.index] === ".") { + this.index++; + while (/\d/.test(this.source[this.index] ?? "")) this.index++; + } + if ((this.source[this.index] ?? "").toLowerCase() === "e") { + this.index++; + if (["+", "-"].includes(this.source[this.index] ?? "")) this.index++; + while (/\d/.test(this.source[this.index] ?? "")) this.index++; + } + return this.source.slice(start, this.index); + } + + private ws() { + while (/\s/.test(this.source[this.index] ?? "")) this.index++; + } + + private expect(ch: string) { + if (this.source[this.index] !== ch) throw new Error(`Expected ${ch} at ${this.index}.`); + this.index++; + } +} + +export function parseJsonSource(source: string): JsonNode { + return new Parser(source).parse(); +} + +export function pointerPart(value: string) { + return value.replaceAll("~", "~0").replaceAll("/", "~1"); +} + +export function leaves(node: JsonNode, pointer = ""): Leaf[] { + if (node.kind === "literal") return [{ pointer, raw: node.raw, valueKind: node.valueKind }]; + if (node.kind === "array") return node.items.flatMap((item, index) => leaves(item, `${pointer}/${index}`)); + return node.entries.flatMap((entry) => leaves(entry.value, `${pointer}/${pointerPart(entry.key)}`)); +} + +export function countLeaves(node: JsonNode) { + return leaves(node).length; +} + +export type DiffKind = "added" | "removed" | "changed" | "same"; + +export function diffLeaves(left: Leaf[], right: Leaf[]) { + const l = new Map(left.map((leaf) => [leaf.pointer, leaf])); + const r = new Map(right.map((leaf) => [leaf.pointer, leaf])); + const pointers = [...new Set([...l.keys(), ...r.keys()])].toSorted(); + return pointers.map((pointer) => { + const before = l.get(pointer); + const after = r.get(pointer); + const kind: DiffKind = before && after ? (before.raw === after.raw ? "same" : "changed") : before ? "removed" : "added"; + return { pointer, before, after, kind }; + }); +} diff --git a/workbench-ui/src/design/components/badges/Badge.tsx b/workbench-ui/src/design/components/badges/Badge.tsx new file mode 100644 index 00000000..bb79a0b9 --- /dev/null +++ b/workbench-ui/src/design/components/badges/Badge.tsx @@ -0,0 +1,67 @@ +import * as Popover from "@radix-ui/react-popover"; +import { Copy } from "lucide-react"; +import type { CSSProperties } from "react"; +import { copyText, cn } from "../../lib"; + +export function InfoBadge({ + label, + colorToken, + meaning, + adr, + evidence, + mono = false, + onCopy, +}: { + label: string; + colorToken: string; + meaning: string; + adr: string; + evidence: string; + mono?: boolean; + onCopy?: string; +}) { + const style = { + backgroundColor: `color-mix(in srgb, var(${colorToken}) 18%, transparent)`, + borderColor: `var(${colorToken})`, + color: `var(${colorToken})`, + } as CSSProperties; + + return ( + + + {label} + + + +
{label}
+

{meaning}

+
+
Pinned by
+
{adr}
+
Evidence example
+
{evidence}
+
+ {onCopy ? ( + + ) : null} +
+
+
+ ); +} diff --git a/workbench-ui/src/design/components/badges/enumCoverage.test.ts b/workbench-ui/src/design/components/badges/enumCoverage.test.ts new file mode 100644 index 00000000..52d1ae8b --- /dev/null +++ b/workbench-ui/src/design/components/badges/enumCoverage.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "vitest"; +import snapshot from "../../../../enum-snapshot.json"; +import { + epistemicStateMeta, + groundingSourceMeta, + normativeClearanceMeta, + reviewStateMeta, +} from "./mappings"; + +function expectExactCoverage(name: string, engineValues: string[], uiValues: string[]) { + expect( + uiValues.toSorted(), + `${name} badge mapping must exactly match engine enum values`, + ).toEqual(engineValues.toSorted()); + expect(new Set(uiValues).size, `${name} badge mapping has duplicate values`).toBe(uiValues.length); +} + +describe("build-time enum coverage", () => { + it("tracks every ratified EpistemicState value exactly once", () => { + expectExactCoverage( + "EpistemicState", + snapshot.EpistemicState, + Object.keys(epistemicStateMeta), + ); + }); + + it("tracks every ratified NormativeClearance value exactly once", () => { + expectExactCoverage( + "NormativeClearance", + snapshot.NormativeClearance, + Object.keys(normativeClearanceMeta), + ); + }); + + it("tracks every ratified ReviewState value exactly once", () => { + expectExactCoverage("ReviewState", snapshot.ReviewState, Object.keys(reviewStateMeta)); + }); + + it("tracks every ratified GroundingSource value exactly once", () => { + expectExactCoverage( + "GroundingSource", + snapshot.GroundingSource, + Object.keys(groundingSourceMeta), + ); + }); +}); diff --git a/workbench-ui/src/design/components/badges/index.tsx b/workbench-ui/src/design/components/badges/index.tsx new file mode 100644 index 00000000..f2ec8562 --- /dev/null +++ b/workbench-ui/src/design/components/badges/index.tsx @@ -0,0 +1,47 @@ +import { + epistemicStateMeta, + groundingSourceMeta, + normativeClearanceMeta, + reviewStateMeta, +} from "./mappings"; +import { InfoBadge } from "./Badge"; +import { EpistemicState, GroundingSource, NormativeClearance, ReviewState } from "./types"; + +export { EpistemicState, GroundingSource, NormativeClearance, ReviewState }; + +export function EpistemicStateBadge({ value }: { value: EpistemicState }) { + return ; +} + +export function NormativeClearanceBadge({ value }: { value: NormativeClearance }) { + return ; +} + +export function ReviewStateBadge({ value }: { value: ReviewState }) { + return ; +} + +export function GroundingSourceBadge({ value }: { value: GroundingSource }) { + return ; +} + +export function TraceHashBadge({ + value, + truncate = 12, +}: { + value: string; + truncate?: number; +}) { + const label = value.slice(0, truncate); + return ( + + ); +} diff --git a/workbench-ui/src/design/components/badges/mappings.ts b/workbench-ui/src/design/components/badges/mappings.ts new file mode 100644 index 00000000..2f7f1a4d --- /dev/null +++ b/workbench-ui/src/design/components/badges/mappings.ts @@ -0,0 +1,48 @@ +import { + EpistemicState, + GroundingSource, + NormativeClearance, + ReviewState, + type BadgeMeta, +} from "./types"; + +export const epistemicStateMeta = { + [EpistemicState.PERCEIVED]: { label: "Perceived", colorToken: "--color-state-perceived", meaning: "Observed at the surface but not yet independently evidenced.", adr: "ADR-0142 / ADR-0162", evidence: "A trace carries raw input evidence." }, + [EpistemicState.EVIDENCED]: { label: "Evidenced", colorToken: "--color-state-evidenced", meaning: "Supported by explicit evidence.", adr: "ADR-0142 / ADR-0162", evidence: "A proposition cites a supporting artifact." }, + [EpistemicState.EVIDENCED_INCOMPLETE]: { label: "Evidenced incomplete", colorToken: "--color-state-evidenced-muted", meaning: "Some evidence exists, but coverage is partial.", adr: "ADR-0142 / ADR-0162", evidence: "Grounding source is partial." }, + [EpistemicState.VERIFIED]: { label: "Verified", colorToken: "--color-state-verified", meaning: "Checked against an authoritative deterministic lane.", adr: "ADR-0142 / ADR-0162", evidence: "Replay hash matches original." }, + [EpistemicState.DECODED]: { label: "Decoded", colorToken: "--color-state-decoded", meaning: "Mapped through ratified semantic structure.", adr: "ADR-0142 / ADR-0162", evidence: "Grounding source is pack, teaching, or vault." }, + [EpistemicState.DECODED_UNARTICULATED]: { label: "Decoded unarticulated", colorToken: "--color-state-decoded-muted", meaning: "Structured internally but not surfaced.", adr: "ADR-0142 / ADR-0162", evidence: "Graph node exists without realized sentence." }, + [EpistemicState.INFERRED]: { label: "Inferred", colorToken: "--color-state-inferred", meaning: "Derived from available structure.", adr: "ADR-0142 / ADR-0162", evidence: "Planner derives a relation from linked propositions." }, + [EpistemicState.UNVERIFIED_POSSIBLE]: { label: "Unverified possible", colorToken: "--color-state-unverified", meaning: "Plausible but not verified.", adr: "ADR-0142 / ADR-0162", evidence: "Candidate has no ratified support yet." }, + [EpistemicState.UNVERIFIED_NOVEL]: { label: "Unverified novel", colorToken: "--color-state-unverified-warm", meaning: "New or OOV content without ratified grounding.", adr: "ADR-0142 / ADR-0162", evidence: "Grounding source is oov." }, + [EpistemicState.CONTRADICTED]: { label: "Contradicted", colorToken: "--color-state-contradicted", meaning: "Evidence conflicts with the proposition.", adr: "ADR-0142 / ADR-0162", evidence: "A boundary or claim rejects the assertion." }, + [EpistemicState.AMBIGUOUS]: { label: "Ambiguous", colorToken: "--color-state-ambiguous", meaning: "Multiple readings remain live.", adr: "ADR-0142 / ADR-0162", evidence: "Parser retains competing interpretations." }, + [EpistemicState.UNDETERMINED]: { label: "Undetermined", colorToken: "--color-state-undetermined", meaning: "No stronger epistemic state is justified.", adr: "ADR-0142 / ADR-0162", evidence: "No grounding source is present." }, + [EpistemicState.SCOPE_BOUNDARY]: { label: "Scope boundary", colorToken: "--color-state-scope", meaning: "The claim is outside the active scope.", adr: "ADR-0142 / ADR-0162", evidence: "Runtime declines to classify beyond scope." }, + [EpistemicState.COMPUTATIONALLY_BOUNDED]: { label: "Computationally bounded", colorToken: "--color-state-bounded", meaning: "The answer is limited by bounded computation.", adr: "ADR-0142 / ADR-0162", evidence: "Eval lane reports a bounded operator." }, + [EpistemicState.EPISTEMIC_STATE_NEEDED]: { label: "State needed", colorToken: "--color-state-needed", meaning: "A missing state must be assigned before trust display.", adr: "ADR-0142 / ADR-0162", evidence: "Unknown grounding source reached UI mapping." }, +} satisfies BadgeMeta; + +export const normativeClearanceMeta = { + [NormativeClearance.CLEARED]: { label: "Cleared", colorToken: "--color-clearance-cleared", meaning: "Runtime-checkable normative gates passed.", adr: "ADR-0142 / ADR-0162", evidence: "Safety and ethics verdicts upheld." }, + [NormativeClearance.VIOLATED]: { label: "Violated", colorToken: "--color-clearance-violated", meaning: "A runtime-checkable boundary failed.", adr: "ADR-0142 / ADR-0162", evidence: "Boundary id appears in normative detail." }, + [NormativeClearance.UNASSESSABLE]: { label: "Unassessable", colorToken: "--color-clearance-unassessable", meaning: "No positive clearance can be asserted.", adr: "ADR-0142 / ADR-0162", evidence: "No runtime-checkable verdict exists." }, + [NormativeClearance.SUPPRESSED]: { label: "Suppressed", colorToken: "--color-clearance-suppressed", meaning: "A refusal surface replaced unsafe output.", adr: "ADR-0142 / ADR-0162", evidence: "refusal_emitted is true." }, +} satisfies BadgeMeta; + +export const reviewStateMeta = { + [ReviewState.PENDING]: { label: "Pending", colorToken: "--color-review-pending", meaning: "Proposal awaits operator review.", adr: "ADR-0057 / ADR-0161 / ADR-0162", evidence: "No terminal transition exists." }, + [ReviewState.ACCEPTED]: { label: "Accepted", colorToken: "--color-review-accepted", meaning: "Operator ratified the proposal.", adr: "ADR-0057 / ADR-0161 / ADR-0162", evidence: "transition.to is accepted." }, + [ReviewState.REJECTED]: { label: "Rejected", colorToken: "--color-review-rejected", meaning: "Operator rejected the proposal.", adr: "ADR-0057 / ADR-0161 / ADR-0162", evidence: "transition.to is rejected." }, + [ReviewState.WITHDRAWN]: { label: "Withdrawn", colorToken: "--color-review-withdrawn", meaning: "Proposal was withdrawn without ratification.", adr: "ADR-0057 / ADR-0161 / ADR-0162", evidence: "transition.to is withdrawn." }, +} satisfies BadgeMeta; + +export const groundingSourceMeta = { + [GroundingSource.PACK]: { label: "Pack", colorToken: "--color-grounding-pack", meaning: "Grounded in a curated semantic pack.", adr: "ADR-0160 / ADR-0162", evidence: "grounding_source is pack." }, + [GroundingSource.TEACHING]: { label: "Teaching", colorToken: "--color-grounding-teaching", meaning: "Grounded in reviewed teaching memory.", adr: "ADR-0160 / ADR-0162", evidence: "grounding_source is teaching." }, + [GroundingSource.VAULT]: { label: "Vault", colorToken: "--color-grounding-vault", meaning: "Grounded in deterministic vault recall.", adr: "ADR-0160 / ADR-0162", evidence: "grounding_source is vault." }, + [GroundingSource.PARTIAL]: { label: "Partial", colorToken: "--color-grounding-partial", meaning: "Only partial grounding was available.", adr: "ADR-0160 / ADR-0162", evidence: "grounding_source is partial." }, + [GroundingSource.OOV]: { label: "OOV", colorToken: "--color-grounding-oov", meaning: "Out-of-vocabulary grounding was encountered.", adr: "ADR-0160 / ADR-0162", evidence: "grounding_source is oov." }, + [GroundingSource.NONE]: { label: "None", colorToken: "--color-grounding-none", meaning: "No grounding source was present.", adr: "ADR-0160 / ADR-0162", evidence: "grounding_source is none." }, +} satisfies BadgeMeta; diff --git a/workbench-ui/src/design/components/badges/types.ts b/workbench-ui/src/design/components/badges/types.ts new file mode 100644 index 00000000..7b895820 --- /dev/null +++ b/workbench-ui/src/design/components/badges/types.ts @@ -0,0 +1,51 @@ +export enum EpistemicState { + PERCEIVED = "perceived", + EVIDENCED = "evidenced", + EVIDENCED_INCOMPLETE = "evidenced_incomplete", + VERIFIED = "verified", + DECODED = "decoded", + DECODED_UNARTICULATED = "decoded_unarticulated", + INFERRED = "inferred", + UNVERIFIED_POSSIBLE = "unverified_possible", + UNVERIFIED_NOVEL = "unverified_novel", + CONTRADICTED = "contradicted", + AMBIGUOUS = "ambiguous", + UNDETERMINED = "undetermined", + SCOPE_BOUNDARY = "scope_boundary", + COMPUTATIONALLY_BOUNDED = "computationally_bounded", + EPISTEMIC_STATE_NEEDED = "epistemic_state_needed", +} + +export enum NormativeClearance { + CLEARED = "cleared", + VIOLATED = "violated", + UNASSESSABLE = "unassessable", + SUPPRESSED = "suppressed", +} + +export enum ReviewState { + PENDING = "pending", + ACCEPTED = "accepted", + REJECTED = "rejected", + WITHDRAWN = "withdrawn", +} + +export enum GroundingSource { + PACK = "pack", + TEACHING = "teaching", + VAULT = "vault", + PARTIAL = "partial", + OOV = "oov", + NONE = "none", +} + +export type BadgeMeta = Record< + T, + { + label: string; + colorToken: string; + meaning: string; + adr: string; + evidence: string; + } +>; diff --git a/workbench-ui/src/design/components/primitives/Button.tsx b/workbench-ui/src/design/components/primitives/Button.tsx new file mode 100644 index 00000000..b0a0b9f6 --- /dev/null +++ b/workbench-ui/src/design/components/primitives/Button.tsx @@ -0,0 +1,30 @@ +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import type { ButtonHTMLAttributes } from "react"; +import { cn } from "../../lib"; + +const buttonVariants = cva( + "inline-flex h-9 items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium transition-colors motion-standard focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-focus-ring)] disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + primary: + "border-[var(--color-border-strong)] bg-[var(--color-surface-overlay)] text-[var(--color-text-primary)] hover:bg-[var(--color-surface-raised)]", + quiet: + "border-[var(--color-border-subtle)] bg-transparent text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]", + }, + }, + defaultVariants: { variant: "primary" }, + }, +); + +export interface ButtonProps + extends ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +export function Button({ className, variant, asChild, ...props }: ButtonProps) { + const Comp = asChild ? Slot : "button"; + return ; +} diff --git a/workbench-ui/src/design/components/primitives/CommandPalette.tsx b/workbench-ui/src/design/components/primitives/CommandPalette.tsx new file mode 100644 index 00000000..bb2c91c6 --- /dev/null +++ b/workbench-ui/src/design/components/primitives/CommandPalette.tsx @@ -0,0 +1,32 @@ +import * as Dialog from "@radix-ui/react-dialog"; +import { Search } from "lucide-react"; +import { EmptyState } from "../states/EmptyState"; + +export function CommandPalette({ + open, + onOpenChange, +}: { + open: boolean; + onOpenChange: (open: boolean) => void; +}) { + return ( + + + + + + + Command Palette + + + Empty command palette stub for the Branch 1 keyboard contract. + + + + + + ); +} diff --git a/workbench-ui/src/design/components/states/EmptyState.tsx b/workbench-ui/src/design/components/states/EmptyState.tsx new file mode 100644 index 00000000..3a299fcc --- /dev/null +++ b/workbench-ui/src/design/components/states/EmptyState.tsx @@ -0,0 +1,18 @@ +import { Button } from "../primitives/Button"; + +export function EmptyState({ + statement, + nextAction, +}: { + statement: string; + nextAction: string; +}) { + return ( +
+

{statement}

+ +
+ ); +} diff --git a/workbench-ui/src/design/components/states/ErrorState.tsx b/workbench-ui/src/design/components/states/ErrorState.tsx new file mode 100644 index 00000000..ef7dc6b1 --- /dev/null +++ b/workbench-ui/src/design/components/states/ErrorState.tsx @@ -0,0 +1,34 @@ +export function ErrorState({ + whatFailed, + mutationStatus, + reproducer, + retrySafety, +}: { + whatFailed: string; + mutationStatus: string; + reproducer: string; + retrySafety: string; +}) { + return ( +
+
+
+
What failed
+
{whatFailed}
+
+
+
Mutation status
+
{mutationStatus}
+
+
+
Reproducer
+
{reproducer}
+
+
+
Retry safety
+
{retrySafety}
+
+
+
+ ); +} diff --git a/workbench-ui/src/design/components/states/LoadingState.tsx b/workbench-ui/src/design/components/states/LoadingState.tsx new file mode 100644 index 00000000..91005ee2 --- /dev/null +++ b/workbench-ui/src/design/components/states/LoadingState.tsx @@ -0,0 +1,16 @@ +export function LoadingState({ label }: { label: string }) { + if (label.trim().toLowerCase() === "thinking...") { + throw new Error('LoadingState label must be specific; "Thinking..." is forbidden.'); + } + + return ( +
+

{label}

+
+
+ ); +} diff --git a/workbench-ui/src/design/components/states/states.test.tsx b/workbench-ui/src/design/components/states/states.test.tsx new file mode 100644 index 00000000..1c41efd8 --- /dev/null +++ b/workbench-ui/src/design/components/states/states.test.tsx @@ -0,0 +1,37 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { EmptyState } from "./EmptyState"; +import { ErrorState } from "./ErrorState"; +import { LoadingState } from "./LoadingState"; + +describe("state components", () => { + it("requires empty-state statement and next action", () => { + render(); + expect(screen.getByText("No trace selected.")).toBeInTheDocument(); + expect(screen.getByRole("button", { name: "Select a trace." })).toBeInTheDocument(); + }); + + it("renders every error-state contract field", () => { + render( + , + ); + expect(screen.getByText("What failed")).toBeInTheDocument(); + expect(screen.getByText("Mutation status")).toBeInTheDocument(); + expect(screen.getByText("Reproducer")).toBeInTheDocument(); + expect(screen.getByText("Retry safety")).toBeInTheDocument(); + }); + + it("requires a specific loading label and caps shimmer cycles", () => { + const consoleError = vi.spyOn(console, "error").mockImplementation(() => undefined); + expect(() => render()).toThrow(); + consoleError.mockRestore(); + render(); + expect(screen.getByText("Loading trace fixture.")).toBeInTheDocument(); + expect(document.querySelector("[data-shimmer-cycles='2']")).toBeInTheDocument(); + }); +}); diff --git a/workbench-ui/src/design/lib.ts b/workbench-ui/src/design/lib.ts new file mode 100644 index 00000000..97a64d12 --- /dev/null +++ b/workbench-ui/src/design/lib.ts @@ -0,0 +1,10 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export async function copyText(value: string) { + await navigator.clipboard?.writeText(value); +} diff --git a/workbench-ui/src/design/tokens/tokens.css b/workbench-ui/src/design/tokens/tokens.css new file mode 100644 index 00000000..1daa0915 --- /dev/null +++ b/workbench-ui/src/design/tokens/tokens.css @@ -0,0 +1,146 @@ +@font-face { + font-family: "Inter"; + src: url("/fonts/Inter-Regular.woff2") format("woff2"); + font-weight: 400 800; + font-display: swap; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2"); + font-weight: 400 800; + font-display: swap; +} + +:root, +.dark { + color-scheme: dark; + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; + + --color-surface-base: #0d1117; + --color-surface-raised: #131923; + --color-surface-overlay: #191f2a; + --color-surface-inset: #070a0f; + --color-border-subtle: #252d3a; + --color-border-strong: #3a4556; + --color-text-primary: #eef2f7; + --color-text-secondary: #aeb8c7; + --color-text-mono: #d8e2ef; + --color-text-muted: #788395; + --color-focus-ring: #7dd3fc; + --color-link: #8bd3ff; + + --color-state-decoded: #4fc3b4; + --color-state-decoded-muted: #3d8f86; + --color-state-verified: #67d391; + --color-state-evidenced: #38bfa7; + --color-state-evidenced-muted: #2f8175; + --color-state-inferred: #8d8cff; + --color-state-unverified: #8aa0b8; + --color-state-unverified-warm: #d39b4f; + --color-state-perceived: #5ec8f2; + --color-state-contradicted: #f07178; + --color-state-ambiguous: #b18cff; + --color-state-undetermined: #77808f; + --color-state-scope: #64748b; + --color-state-bounded: #e59f48; + --color-state-needed: #f3c45b; + + --color-clearance-cleared: #74d99f; + --color-clearance-violated: #f25f6f; + --color-clearance-unassessable: #7f8998; + --color-clearance-suppressed: #bd5b68; + + --color-review-pending: #e8b94e; + --color-review-accepted: #57c989; + --color-review-rejected: #cf6470; + --color-review-withdrawn: #858d9b; + + --color-grounding-teaching: #61d394; + --color-grounding-pack: #42c7b5; + --color-grounding-vault: #7f95ff; + --color-grounding-partial: #6faea4; + --color-grounding-oov: #e9a94f; + --color-grounding-none: #6f7b8c; + + --color-state-info-bg: #10253a; + --color-state-info-border: #23648e; + --color-state-info-text: #bde8ff; + --color-state-success-bg: #10291e; + --color-state-success-border: #2d7a55; + --color-state-success-text: #b9f3d3; + --color-state-warning-bg: #31240c; + --color-state-warning-border: #9c6a1b; + --color-state-warning-text: #ffd89a; + --color-state-danger-bg: #351419; + --color-state-danger-border: #9f3848; + --color-state-danger-text: #ffc3ca; + --color-state-neutral-bg: #1b212c; + --color-state-neutral-border: #465164; + --color-state-neutral-text: #d4dbe7; + --color-state-purple-bg: #241a34; + --color-state-purple-border: #7655a2; + --color-state-purple-text: #dec8ff; + + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-10: 2.5rem; + --space-12: 3rem; + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-1: var(--radius-sm); + --radius-2: var(--radius-md); + --radius-3: var(--radius-lg); + --shadow-panel: 0 10px 35px rgb(0 0 0 / 0.28); + --shadow-floating: 0 18px 60px rgb(0 0 0 / 0.45); + --shadow-overlay: var(--shadow-floating); + + --font-size-display: 1.75rem; + --font-size-h1: 1.375rem; + --font-size-h2: 1rem; + --font-size-body: 0.875rem; + --font-size-meta: 0.75rem; + --font-size-mono: 0.75rem; + --font-weight-regular: 400; + --font-weight-medium: 500; + --font-weight-semibold: 650; + --line-height-tight: 1.2; + --line-height-base: 1.5; + --text-xs: var(--font-size-meta); + --text-sm: var(--font-size-body); + --text-md: 1rem; + --text-lg: 1.125rem; + --text-xl: var(--font-size-h1); + --line-tight: var(--line-height-tight); + --line-normal: var(--line-height-base); + + --motion-instant: 0ms; + --motion-fast: 120ms; + --motion-base: 180ms; + --motion-slow: 1200ms; + --motion-ease-out: cubic-bezier(0.2, 0, 0, 1); + --motion-ease-spring: cubic-bezier(0.16, 1, 0.3, 1); + --motion-duration-instant: var(--motion-instant); + --motion-duration-fast: var(--motion-fast); + --motion-duration-standard: var(--motion-base); + --motion-duration-slow: var(--motion-slow); + --motion-ease-standard: var(--motion-ease-out); + --motion-ease-emphasized: var(--motion-ease-spring); +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: var(--motion-duration-instant) !important; + transition-duration: var(--motion-duration-instant) !important; + animation-iteration-count: 1 !important; + } +} diff --git a/workbench-ui/src/design/tokens/tokens.test.ts b/workbench-ui/src/design/tokens/tokens.test.ts new file mode 100644 index 00000000..4062ac82 --- /dev/null +++ b/workbench-ui/src/design/tokens/tokens.test.ts @@ -0,0 +1,37 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; +import { tokens } from "./tokens"; + +function parseCssTokens() { + const css = readFileSync(resolve(process.cwd(), "src/design/tokens/tokens.css"), "utf8"); + return Object.fromEntries( + [...css.matchAll(/--([a-z0-9-]+):\s*([^;]+);/g)].map(([, name, value]) => [ + name, + value.trim(), + ]), + ); +} + +describe("design tokens", () => { + it("keeps tokens.css and generated tokens.ts synchronized", () => { + expect(tokens).toEqual(parseCssTokens()); + }); + + it("collapses tokenized motion under reduced motion", () => { + const reduced = true; + vi.stubGlobal("matchMedia", (query: string) => ({ + matches: reduced && query.includes("prefers-reduced-motion"), + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + })); + + expect(window.matchMedia("(prefers-reduced-motion: reduce)").matches).toBe(true); + expect(tokens["motion-instant"]).toBe("0ms"); + }); +}); diff --git a/workbench-ui/src/design/tokens/tokens.ts b/workbench-ui/src/design/tokens/tokens.ts new file mode 100644 index 00000000..e81893c7 --- /dev/null +++ b/workbench-ui/src/design/tokens/tokens.ts @@ -0,0 +1,115 @@ +// Generated by scripts/generate-tokens.ts. Do not edit by hand. +export const tokens = { + "font-sans": "\"Inter\", ui-sans-serif, system-ui, sans-serif", + "font-mono": "\"JetBrains Mono\", ui-monospace, SFMono-Regular, monospace", + "color-surface-base": "#0d1117", + "color-surface-raised": "#131923", + "color-surface-overlay": "#191f2a", + "color-surface-inset": "#070a0f", + "color-border-subtle": "#252d3a", + "color-border-strong": "#3a4556", + "color-text-primary": "#eef2f7", + "color-text-secondary": "#aeb8c7", + "color-text-mono": "#d8e2ef", + "color-text-muted": "#788395", + "color-focus-ring": "#7dd3fc", + "color-link": "#8bd3ff", + "color-state-decoded": "#4fc3b4", + "color-state-decoded-muted": "#3d8f86", + "color-state-verified": "#67d391", + "color-state-evidenced": "#38bfa7", + "color-state-evidenced-muted": "#2f8175", + "color-state-inferred": "#8d8cff", + "color-state-unverified": "#8aa0b8", + "color-state-unverified-warm": "#d39b4f", + "color-state-perceived": "#5ec8f2", + "color-state-contradicted": "#f07178", + "color-state-ambiguous": "#b18cff", + "color-state-undetermined": "#77808f", + "color-state-scope": "#64748b", + "color-state-bounded": "#e59f48", + "color-state-needed": "#f3c45b", + "color-clearance-cleared": "#74d99f", + "color-clearance-violated": "#f25f6f", + "color-clearance-unassessable": "#7f8998", + "color-clearance-suppressed": "#bd5b68", + "color-review-pending": "#e8b94e", + "color-review-accepted": "#57c989", + "color-review-rejected": "#cf6470", + "color-review-withdrawn": "#858d9b", + "color-grounding-teaching": "#61d394", + "color-grounding-pack": "#42c7b5", + "color-grounding-vault": "#7f95ff", + "color-grounding-partial": "#6faea4", + "color-grounding-oov": "#e9a94f", + "color-grounding-none": "#6f7b8c", + "color-state-info-bg": "#10253a", + "color-state-info-border": "#23648e", + "color-state-info-text": "#bde8ff", + "color-state-success-bg": "#10291e", + "color-state-success-border": "#2d7a55", + "color-state-success-text": "#b9f3d3", + "color-state-warning-bg": "#31240c", + "color-state-warning-border": "#9c6a1b", + "color-state-warning-text": "#ffd89a", + "color-state-danger-bg": "#351419", + "color-state-danger-border": "#9f3848", + "color-state-danger-text": "#ffc3ca", + "color-state-neutral-bg": "#1b212c", + "color-state-neutral-border": "#465164", + "color-state-neutral-text": "#d4dbe7", + "color-state-purple-bg": "#241a34", + "color-state-purple-border": "#7655a2", + "color-state-purple-text": "#dec8ff", + "space-1": "0.25rem", + "space-2": "0.5rem", + "space-3": "0.75rem", + "space-4": "1rem", + "space-5": "1.25rem", + "space-6": "1.5rem", + "space-8": "2rem", + "space-10": "2.5rem", + "space-12": "3rem", + "radius-sm": "0.25rem", + "radius-md": "0.375rem", + "radius-lg": "0.5rem", + "radius-1": "var(--radius-sm)", + "radius-2": "var(--radius-md)", + "radius-3": "var(--radius-lg)", + "shadow-panel": "0 10px 35px rgb(0 0 0 / 0.28)", + "shadow-floating": "0 18px 60px rgb(0 0 0 / 0.45)", + "shadow-overlay": "var(--shadow-floating)", + "font-size-display": "1.75rem", + "font-size-h1": "1.375rem", + "font-size-h2": "1rem", + "font-size-body": "0.875rem", + "font-size-meta": "0.75rem", + "font-size-mono": "0.75rem", + "font-weight-regular": "400", + "font-weight-medium": "500", + "font-weight-semibold": "650", + "line-height-tight": "1.2", + "line-height-base": "1.5", + "text-xs": "var(--font-size-meta)", + "text-sm": "var(--font-size-body)", + "text-md": "1rem", + "text-lg": "1.125rem", + "text-xl": "var(--font-size-h1)", + "line-tight": "var(--line-height-tight)", + "line-normal": "var(--line-height-base)", + "motion-instant": "0ms", + "motion-fast": "120ms", + "motion-base": "180ms", + "motion-slow": "1200ms", + "motion-ease-out": "cubic-bezier(0.2, 0, 0, 1)", + "motion-ease-spring": "cubic-bezier(0.16, 1, 0.3, 1)", + "motion-duration-instant": "var(--motion-instant)", + "motion-duration-fast": "var(--motion-fast)", + "motion-duration-standard": "var(--motion-base)", + "motion-duration-slow": "var(--motion-slow)", + "motion-ease-standard": "var(--motion-ease-out)", + "motion-ease-emphasized": "var(--motion-ease-spring)" +} as const; + +export type DesignTokenName = keyof typeof tokens; +export type DesignTokenValue = (typeof tokens)[DesignTokenName]; diff --git a/workbench-ui/src/index.css b/workbench-ui/src/index.css new file mode 100644 index 00000000..8ddb265b --- /dev/null +++ b/workbench-ui/src/index.css @@ -0,0 +1,35 @@ +@import "./design/tokens/tokens.css"; + +@tailwind base; +@tailwind components; +@tailwind utilities; + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: var(--color-surface-base); + color: var(--color-text-primary); + font-family: var(--font-sans); + line-height: var(--line-normal); +} + +button, +a, +[tabindex]:not([tabindex="-1"]) { + outline: none; +} + +:focus-visible { + outline: 2px solid var(--color-focus-ring); + outline-offset: 2px; +} + +.motion-standard { + transition-duration: var(--motion-duration-standard); + transition-timing-function: var(--motion-ease-standard); +} diff --git a/workbench-ui/src/main.tsx b/workbench-ui/src/main.tsx new file mode 100644 index 00000000..e86d0f79 --- /dev/null +++ b/workbench-ui/src/main.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import { PreviewPage } from "./preview/PreviewPage"; + +function App() { + return window.location.pathname === "/preview" ? : ; +} + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/workbench-ui/src/preview/PreviewPage.test.tsx b/workbench-ui/src/preview/PreviewPage.test.tsx new file mode 100644 index 00000000..7630a15f --- /dev/null +++ b/workbench-ui/src/preview/PreviewPage.test.tsx @@ -0,0 +1,35 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it } from "vitest"; +import { PreviewPage } from "./PreviewPage"; + +describe("/preview", () => { + it("renders every primitive with network unavailable", () => { + vi.stubGlobal("fetch", vi.fn(() => Promise.reject(new Error("network blocked")))); + render(); + expect(screen.getByRole("heading", { name: "CORE Workbench Design System v1" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Primitives" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Badges" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "States" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Stable JSON Viewer" })).toBeInTheDocument(); + expect(fetch).not.toHaveBeenCalled(); + }); + + it("opens CommandPalette with mod+k and closes overlays with Escape", async () => { + const user = userEvent.setup(); + render(); + await user.keyboard("{Control>}k{/Control}"); + expect(screen.getByRole("dialog", { name: "Command Palette" })).toBeInTheDocument(); + await user.keyboard("{Escape}"); + expect(screen.queryByRole("dialog", { name: "Command Palette" })).not.toBeInTheDocument(); + }); + + it("keeps tab order in DOM order for preview actions", async () => { + const user = userEvent.setup(); + render(); + await user.tab(); + expect(screen.getByRole("button", { name: "Primary action" })).toHaveFocus(); + await user.tab(); + expect(screen.getByRole("button", { name: "Quiet action" })).toHaveFocus(); + }); +}); diff --git a/workbench-ui/src/preview/PreviewPage.tsx b/workbench-ui/src/preview/PreviewPage.tsx new file mode 100644 index 00000000..1009339e --- /dev/null +++ b/workbench-ui/src/preview/PreviewPage.tsx @@ -0,0 +1,85 @@ +import { useEffect, useState } from "react"; +import { StableJsonViewer } from "../design/components/StableJsonViewer"; +import { + EpistemicState, + EpistemicStateBadge, + GroundingSource, + GroundingSourceBadge, + NormativeClearance, + NormativeClearanceBadge, + ReviewState, + ReviewStateBadge, + TraceHashBadge, +} from "../design/components/badges"; +import { Button } from "../design/components/primitives/Button"; +import { CommandPalette } from "../design/components/primitives/CommandPalette"; +import { EmptyState } from "../design/components/states/EmptyState"; +import { ErrorState } from "../design/components/states/ErrorState"; +import { LoadingState } from "../design/components/states/LoadingState"; + +export function PreviewPage() { + const [paletteOpen, setPaletteOpen] = useState(false); + + useEffect(() => { + const onKeyDown = (event: KeyboardEvent) => { + if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") { + event.preventDefault(); + setPaletteOpen(true); + } + if (event.key === "Escape") setPaletteOpen(false); + }; + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, []); + + return ( +
+
+

CORE Workbench Design System v1

+

+ Static Branch 1 substrate preview. No backend, no API client, no runtime mutation surface. +

+
+ +
+

Primitives

+
+ + +
+
+ +
+

Badges

+
+ {Object.values(EpistemicState).map((value) => )} + {Object.values(NormativeClearance).map((value) => )} + {Object.values(ReviewState).map((value) => )} + {Object.values(GroundingSource).map((value) => )} + +
+
+ +
+

States

+
+ + + +
+
+ +
+

Stable JSON Viewer

+ +
+ + +
+ ); +} diff --git a/workbench-ui/src/test/setup.ts b/workbench-ui/src/test/setup.ts new file mode 100644 index 00000000..08fce702 --- /dev/null +++ b/workbench-ui/src/test/setup.ts @@ -0,0 +1,8 @@ +import "@testing-library/jest-dom/vitest"; + +Object.defineProperty(navigator, "clipboard", { + configurable: true, + value: { + writeText: vi.fn().mockResolvedValue(undefined), + }, +}); diff --git a/workbench-ui/tailwind.config.ts b/workbench-ui/tailwind.config.ts new file mode 100644 index 00000000..12a777c2 --- /dev/null +++ b/workbench-ui/tailwind.config.ts @@ -0,0 +1,15 @@ +import type { Config } from "tailwindcss"; + +export default { + darkMode: "class", + content: ["./index.html", "./src/**/*.{ts,tsx}"], + theme: { + extend: { + fontFamily: { + sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"], + mono: ["JetBrains Mono", "ui-monospace", "SFMono-Regular", "monospace"], + }, + }, + }, + plugins: [], +} satisfies Config; diff --git a/workbench-ui/tsconfig.json b/workbench-ui/tsconfig.json new file mode 100644 index 00000000..39136000 --- /dev/null +++ b/workbench-ui/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2023", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ES2023"], + "types": ["vitest/globals"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src", "scripts"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/workbench-ui/tsconfig.node.json b/workbench-ui/tsconfig.node.json new file mode 100644 index 00000000..f25d7bb5 --- /dev/null +++ b/workbench-ui/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES2023", + "lib": ["ES2023"], + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts", "scripts/*.ts"] +} diff --git a/workbench-ui/vite.config.ts b/workbench-ui/vite.config.ts new file mode 100644 index 00000000..722961b7 --- /dev/null +++ b/workbench-ui/vite.config.ts @@ -0,0 +1,11 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [react()], + test: { + environment: "happy-dom", + setupFiles: ["./src/test/setup.ts"], + globals: true, + }, +});