diff --git a/.gitignore b/.gitignore index 907677e..d6fa558 100644 --- a/.gitignore +++ b/.gitignore @@ -68,21 +68,33 @@ paper/figures/ # Paper workflow outputs examples/paper_runs/paper_*/results/ +/paper_04/ +/figures/ +examples/paper_runs/paper_05/ibm_credentials.local.json # LaTeX artifacts and drafts *.tex +/paper_*.bib # Local manuscript/support files kept out of VCS lidmas.bib live demo us-patent/ +/.local_artifacts/ +/run_lidmas.sh +/simulator-data-source-reversed.png # Local PhD proposal workspace phd_tubaf/ +# Local startup/commercialization notes +/startup/ +gottesman-startup-web/ + # Local slide/PPT workspace artifacts .venv_ppt/ *_talk.pptx +*_talk*.pptx # Rust backend build artifacts lidmas+/target/ @@ -96,9 +108,10 @@ lidmas+/frontend/*.tsbuildinfo lidmas+/design/*.png lidmas+/design/*.pdf -# Private LiDMaS+ commercialization workspace -lidmas+/ +# LiDMaS+ generated/local artifacts +lidmas+/dashboard_metric_trust_classification.txt lidmas+/design/ -lidmas+/frontend/ -lidmas+/README.md -gottesman-startup-web/ +lidmas+/desktop-swiftui/.build/ + +# Large local data slices +/hardware_integration/xanadu/sliced_real_data_1gb/ diff --git a/lidmas+/README.md b/lidmas+/README.md new file mode 100644 index 0000000..00b301e --- /dev/null +++ b/lidmas+/README.md @@ -0,0 +1,237 @@ +# LiDMaS+ Open-Source UI + +Version target: `v1.2.2` + +This folder contains the open-source web UI for LiDMaS+. The UI is built against a backend service contract for decoder studies, replay workflows, validation views, provider metadata, and lab-facing integration sessions. + +## Scope + +- Provider registry and metadata (`/providers`) +- Job orchestration metadata (`/jobs`) +- Run tracking and artifact lineage (`/runs`) +- Adapter session orchestration for hardware/replay flows (`/integrations/sessions`) +- Provider-output integrity validation (`/providers/{id}/validate`) +- Research system log scanning (`/system/logscan`) +- Health and uptime (`/health`) + +Storage and runtime policy are owned by the backend service. The frontend can run against a local or hosted `VITE_API_BASE_URL`. + +## Frontend + +A React + TypeScript GUI scaffold is available in: + +`lidmas+/frontend` + +Run it with: + +```bash +cd "lidmas+/frontend" +npm install +npm run dev +``` + +## Backend Target + +```bash +VITE_API_BASE_URL=http://127.0.0.1:8080/api/v1 npm run dev +``` + +Default frontend URL: `http://127.0.0.1:5173` +Default backend target: `http://127.0.0.1:8080/api/v1` + +## API Root + +- `GET /api/v1/health` + +## paper_04 App Parity Endpoints + +LiDMaS+ now exposes app-driven `paper_04` execution and manifest parity checks against the canonical CLI workflow (`./examples/paper_runs/paper_04/run_all.sh`). + +- `GET /api/v1/system/paper_04/manifest` +- `POST /api/v1/system/paper_04/run` + +`POST /run` executes the same `paper_04` runner script and returns: + +- execution status (`succeeded`, `failed`, `timeout`, or `parity_mismatch`) +- stdout/stderr tails +- deterministic artifact manifest hash over key `paper_04/results` outputs +- optional parity comparison (`compare_with_manifest_hash`) + +## Authentication + +Most `/api/v1/*` endpoints now require bearer authentication. + +Public auth endpoints: + +- `POST /api/v1/auth/signup` +- `POST /api/v1/auth/signin` + +Protected auth endpoints: + +- `GET /api/v1/auth/me` +- `POST /api/v1/auth/signout` + +Example sign-in: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/auth/signin \ + -H "content-type: application/json" \ + -d '{"email":"you@example.org","password":"your-password"}' +``` + +Use returned `token` as: + +```bash +-H "authorization: Bearer " +``` + +`POST /api/v1/runs/{run_id}/telemetry` remains open for adapter push workflows. + +## Example Flow + +1. Register provider: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/providers \ + -H "content-type: application/json" \ + -d '{ + "name":"Xanadu", + "kind":"photonic", + "supported_formats":["aurora_switch_dir","shot_matrix","count_table_json"], + "contact_email":"integration@example.org", + "notes":"provider integration test" + }' +``` + +2. Create job: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/jobs \ + -H "content-type: application/json" \ + -d '{ + "provider_id":"", + "dataset_label":"aurora_batch0_qpu5", + "decoders":["mwpm","uf","bp","neural_mwpm"], + "priority":5 + }' +``` + +3. Validate provider result integrity: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/providers//validate \ + -H "content-type: application/json" \ + -d '{ + "dataset_label":"aurora_batch0_qpu5", + "request_lines":500, + "response_lines":500, + "request_parse_errors":0, + "response_parse_errors":0, + "decoder_name_mismatch_count":0, + "warning_no_syndrome_count":346 + }' +``` + +4. Create run: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/runs \ + -H "content-type: application/json" \ + -d '{ + "provider_id":"", + "dataset_label":"aurora_batch0_qpu5", + "decoders":["mwpm","uf","bp","neural_mwpm"] + }' +``` + +5. Attach artifact: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/runs//artifacts \ + -H "content-type: application/json" \ + -d '{ + "name":"figure_A_decoder_tradeoff_scatter", + "kind":"figure_pdf", + "path":"examples/paper_runs/paper_03/results/08_extended_analysis/figures/figure_A_decoder_tradeoff_scatter.pdf", + "sha256":"" + }' +``` + +6. Run research log scan: + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/system/logscan \ + -H "content-type: application/json" \ + -d '{ + "logs":[ + { + "timestamp":"2026-04-20 15:28:02.341", + "level":"ERROR", + "message":"Provider-03 returned timeout during parity check replay", + "source":"providers/health.rs:156" + } + ], + "max_findings":50 + }' +``` + +7. Start integration session (IBM live example): + +```bash +curl -s -X POST http://127.0.0.1:8080/api/v1/integrations/sessions \ + -H "content-type: application/json" \ + -d '{ + "run_id":"", + "adapter_id":"ibm_superconducting_live", + "config":{ + "backend_name":"ibm_kingston", + "poll_interval":30 + } + }' +``` + +8. Tail integration session logs: + +```bash +curl -s "http://127.0.0.1:8080/api/v1/integrations/sessions//logs?tail=100" +``` + +## Adapter Session Endpoints + +- `GET /api/v1/integrations/sessions` +- `POST /api/v1/integrations/sessions` +- `GET /api/v1/integrations/sessions/{session_id}` +- `POST /api/v1/integrations/sessions/{session_id}/stop` +- `GET /api/v1/integrations/sessions/{session_id}/logs?tail=200` +- `POST /api/v1/system/credentials/ibm` (store IBM API key in runtime memory) + +Supported adapter ids: + +- `ibm_superconducting_live` +- `ankaa_superconducting_replay` +- `xanadu_gkp_remote_replay` + +IBM auth note: + +- Adapter session payloads do not carry API keys. +- The frontend may submit an IBM key to `/system/credentials/ibm`, where the backend stores it in runtime memory for the current service session. +- If no runtime key is provided, the backend may use `IBM_QUANTUM_API_KEY` or a saved `qiskit-ibm-runtime` account. + +## Troubleshooting + +If frontend shows `Backend missing /integrations/sessions (old server)`, your UI is connected to an older backend build. +Restart the backend service and verify: + +```bash +curl -i http://127.0.0.1:8080/api/v1/integrations/sessions +``` + +Expected status is `200` (not `404`). + +## Next Steps + +- Persist state to PostgreSQL (providers/jobs/runs/artifacts tables) +- Add auth/roles for provider portal vs researcher views +- Add async worker queue for real run execution +- Add run logs/streaming endpoint for GUI live monitor +- Add schema versioning + provider plugin registry contract diff --git a/lidmas+/frontend/.env.example b/lidmas+/frontend/.env.example new file mode 100644 index 0000000..d224cca --- /dev/null +++ b/lidmas+/frontend/.env.example @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://127.0.0.1:8080/api/v1 diff --git a/lidmas+/frontend/.gitignore b/lidmas+/frontend/.gitignore new file mode 100644 index 0000000..214eade --- /dev/null +++ b/lidmas+/frontend/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.DS_Store +*.tsbuildinfo diff --git a/lidmas+/frontend/README.md b/lidmas+/frontend/README.md new file mode 100644 index 0000000..83c1d2e --- /dev/null +++ b/lidmas+/frontend/README.md @@ -0,0 +1,103 @@ +# LiDMaS+ Frontend (React + TypeScript) + +This frontend is an open-source research UI scaffold for the LiDMaS+ control plane. + +## Features in scaffold + +- App shell with top mode chips + persistent sidebar navigation +- Auth gate overlay (Sign In / Sign Up) before workspace access +- Post-login IBM onboarding popup for optional live-noise key setup +- Routed modules: + - Dashboard + - Providers + - Jobs Queue + - Runs & Logs + - Analysis + - Artifacts + - Conformance + - Settings +- API integration against Rust backend `/api/v1` endpoints +- React Query data fetching + mutations +- Structured components ready for research-workspace expansion +- Jobs page Adapter Sessions panel: + - provider-family launcher (`IBM`, `Ankaa`, `Xanadu`) + - automatic adapter selection + - optional auto-create run/provider when no run is selected + - stop and log-tail actions for active sessions + +## Run + +```bash +cd "lidmas+/frontend" +npm install +npm run dev +``` + +Default frontend URL: `http://127.0.0.1:5173` +Default backend target: `http://127.0.0.1:8080/api/v1` + +To use a different backend: + +```bash +VITE_API_BASE_URL=http://127.0.0.1:8080/api/v1 npm run dev +``` + +## Adapter Session Flow + +1. Set data mode to `Live API`. +2. Open `Jobs & Runs`. +3. Click `+ Start Adapter Session`. +4. Pick provider family (`IBM`, `Ankaa`, or `Xanadu`) and fill provider-specific fields. +5. Start session and monitor from `Adapter Sessions` table (status/logs/stop). + +Provider-to-adapter mapping is automatic: + +- `IBM` -> `ibm_superconducting_live` +- `Ankaa` -> `ankaa_superconducting_replay` +- `Xanadu` -> `xanadu_gkp_remote_replay` + +IBM authentication behavior: + +- After login, frontend can collect IBM API key and submit it to backend runtime memory (`POST /api/v1/system/credentials/ibm`). +- Backend injects stored key into IBM adapter session process env (`IBM_QUANTUM_API_KEY`). +- If no key is provided, backend falls back to existing runtime env/saved Qiskit credentials. +- Missing credentials surface as adapter session failure logs. + +Backend auth behavior: + +- Frontend sign-in/sign-up is now backed by backend auth endpoints (`/api/v1/auth/*`). +- API requests include bearer token automatically after successful login. + +## paper_04 CLI/IDE Parity Flow + +From `Runs` page: + +1. Click `Capture CLI Baseline` after generating `paper_04` from CLI (or existing artifacts). +2. Click `Run paper_04 In LiDMaS+` to execute the same workflow through backend. +3. Compare returned manifest parity (`MATCH` / `MISMATCH`). + +Backend endpoints used: + +- `GET /api/v1/system/paper_04/manifest` +- `POST /api/v1/system/paper_04/run` + +## Troubleshooting + +If Adapter Sessions shows: + +- `Backend missing /integrations/sessions (old server)` + +your frontend is pointed to an older backend binary. Restart backend from latest source and verify: + +```bash +curl -i http://127.0.0.1:8080/api/v1/integrations/sessions +``` + +Expected response: `200` with JSON array. + +## Next recommended additions + +1. Auth (OIDC/JWT) and route guards by role. +2. Real-time logs/telemetry via SSE or WebSocket. +3. Rich tables and charting for figure/table previews. +4. Provider mapping editor with schema-diff preview. diff --git a/lidmas+/frontend/SETUP.md b/lidmas+/frontend/SETUP.md new file mode 100644 index 0000000..3c7414b --- /dev/null +++ b/lidmas+/frontend/SETUP.md @@ -0,0 +1,271 @@ +# LiDMaS+ Frontend - Open-Source Research Setup + +## Adapter Sessions (Current Runtime Flow) + +Recommended frontend launch: + +```bash +cd "lidmas+/frontend" +npm install +VITE_API_BASE_URL=http://127.0.0.1:8080/api/v1 npm run dev +``` + +The frontend targets the backend service configured by `VITE_API_BASE_URL`. + +Authentication: + +- UI now enforces backend sign-in/sign-up before opening workspace. +- Protected API calls use bearer token issued by backend (`/api/v1/auth/signin` or `/api/v1/auth/signup`). + +The Jobs page includes an `Adapter Sessions` control surface for launching provider flows: + +- IBM live superconducting telemetry +- Ankaa superconducting replay +- Xanadu remote GKP replay (SSH) + +Launcher behavior: + +- Operator chooses provider family first (`IBM`, `Ankaa`, `Xanadu`) +- Frontend maps provider family to adapter id automatically +- If no run is selected, frontend auto-creates provider/run before launch + +Backend requirement: + +- `/api/v1/integrations/sessions` endpoints must be available from the backend configured in `VITE_API_BASE_URL`. + +Quick check: + +```bash +curl -i http://127.0.0.1:8080/api/v1/integrations/sessions +``` + +If this returns `404`, the frontend is connected to an older backend build. + +## Architecture Overview + +This frontend uses a modern open-source research UI stack: + +### Core Dependencies +- **React 18** - UI framework +- **React Router v6** - Client-side routing +- **TypeScript** - Type safety +- **Vite** - Build tool & dev server +- **TanStack Query** - Server state management + +### UI & Styling +- **Tailwind CSS v4** - Utility-first CSS framework +- **Shadcn/ui components** - Pre-built, accessible React components +- **Lucide React** - Beautiful icon library +- **CVA (class-variance-authority)** - Component variant management + +### Forms & Data +- **React Hook Form** - Lightweight form state management +- **Recharts** - Data visualization & charts (for telemetry, analysis) +- **Zustand** - Simple state management (alternative to Redux) + +## File Structure + +``` +frontend/ +├── src/ +│ ├── components/ +│ │ └── ui/ # Shadcn/ui components (Button, Card, Input, etc.) +│ ├── lib/ +│ │ └── utils.ts # Utility functions (cn helper for Tailwind) +│ ├── ui/ # Page components (AppShell, DashboardPage, etc.) +│ ├── api/ # API client & hooks +│ ├── router/ # Route definitions +│ ├── styles.css # Global styles (includes Tailwind directives) +│ └── main.tsx # App entry point +├── tailwind.config.ts # Tailwind configuration +├── postcss.config.js # PostCSS plugins +├── vite.config.ts # Vite configuration with path alias (@) +└── tsconfig.json # TypeScript config with @ path alias +``` + +## Key Features + +### 1. Path Alias (@) +Import files using `@` instead of relative paths: +```typescript +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +``` + +### 2. Tailwind CSS Integration +- Uses **HSL CSS variables** for color theming +- Maintains **backward compatibility** with existing design tokens +- Supports **dark mode** (can be extended in tailwind.config.ts) + +### 3. Component Library +Pre-built components in `src/components/ui/`: +- `Button` - Variants: default, destructive, outline, secondary, ghost, link +- `Card` - CardHeader, CardContent, CardFooter, CardTitle, CardDescription +- `Input` - Text input with focus states +- `Select` - Dropdown with Tailwind styling +- `Badge` - Inline status badges with variants +- `Table` - Full-featured data table components + +### 4. Accessible Components +All components follow WAI-ARIA standards with: +- Keyboard navigation support +- Focus management +- Screen reader compatible + +## Development Workflow + +### Starting the dev server: +```bash +npm run dev # or +npm run lidmas # Opens in Chrome automatically +``` + +### Building for production: +```bash +npm run build # Builds to dist/ +npm run preview # Preview production build locally +``` + +### TypeScript checking: +```bash +npm run build # Includes `tsc --noEmit` type checking +``` + +## Migrating Existing Components + +### From custom CSS to Shadcn/ui + Tailwind: + +**Before (Custom CSS):** +```tsx +
+

Title

+

Subtitle

+
+``` + +**After (Tailwind + Shadcn):** +```tsx +import { Card, CardTitle, CardDescription } from "@/components/ui/card" + + + Title + Subtitle + +``` + +### Using Tailwind Classes: +```tsx + + +// Or using our Button component +import { Button } from "@/components/ui/button" + +``` + +## Color System + +### CSS Variables (defined in styles.css) +```css +--primary: 210 100% 50%; /* Blue */ +--secondary: 120 100% 35%; /* Green */ +--destructive: 0 84% 60%; /* Red */ +--accent: 36 100% 50%; /* Orange */ +--muted: 210 10% 35%; /* Gray */ +``` + +### Usage in Tailwind: +```html + +
Primary
+
Destructive
+``` + +## Adding New Components + +Create a new component in `src/components/ui/example.tsx`: +```typescript +import * as React from "react" +import { cn } from "@/lib/utils" + +const Example = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ) +) +Example.displayName = "Example" + +export { Example } +``` + +Then export from `src/components/ui/index.ts`: +```typescript +export { Example } from "./example" +``` + +## Form Handling with React Hook Form + +```typescript +import { useForm } from "react-hook-form" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" + +export function MyForm() { + const { register, handleSubmit } = useForm() + + return ( +
+ + +
+ ) +} +``` + +## Data Visualization with Recharts + +For your telemetry, analysis, and dashboard pages: +```typescript +import { LineChart, Line, XAxis, YAxis, CartesianGrid } from "recharts" + +const data = [{ time: "00:00", value: 100 }, { time: "01:00", value: 120 }] + +export function Chart() { + return ( + + + + + + + ) +} +``` + +## Next Steps + +1. **Create example pages** - Convert one dashboard page to use new components +2. **Add icons** - Import from `lucide-react` for consistent iconography +3. **Form pages** - Update Settings/Conformance pages with React Hook Form +4. **Charts** - Add Recharts visualizations to Telemetry/Analysis pages +5. **State management** - Consider Zustand for complex state + +## Debugging + +### Tailwind classes not working? +1. Check that `src/styles.css` is imported in `src/main.tsx` +2. Verify class names are correct (Tailwind only generates classes in content paths) +3. Check `tailwind.config.ts` includes your file paths + +### Component not found? +1. Make sure it's exported from `src/components/ui/index.ts` +2. Check the import path uses `@/` alias + +### TypeScript errors? +1. Run `npm run build` to see type errors +2. Check `tsconfig.json` paths configuration diff --git a/lidmas+/frontend/index.html b/lidmas+/frontend/index.html new file mode 100644 index 0000000..2d854b4 --- /dev/null +++ b/lidmas+/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + LiDMaS+ Control Plane + + +
+ + + diff --git a/lidmas+/frontend/package-lock.json b/lidmas+/frontend/package-lock.json new file mode 100644 index 0000000..2e3608e --- /dev/null +++ b/lidmas+/frontend/package-lock.json @@ -0,0 +1,3031 @@ +{ + "name": "lidmas-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lidmas-frontend", + "version": "0.1.0", + "dependencies": { + "@tanstack/react-query": "^5.59.20", + "lucide-react": "^1.8.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.72.1", + "react-router-dom": "^6.30.1", + "recharts": "^3.8.1", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.2.2", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.7.0", + "autoprefixer": "^10.5.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "postcss": "^8.5.10", + "tailwind-merge": "^3.5.0", + "tailwindcss": "^4.2.2", + "typescript": "^5.6.3", + "vite": "^5.4.10" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz", + "integrity": "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", + "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "postcss": "^8.5.6", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.96.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.96.2.tgz", + "integrity": "sha512-hzI6cTVh4KNRk8UtoIBS7Lv9g6BnJPXvBKsvYH1aGWvv0347jT3BnSvztOE+kD76XGvZnRC/t6qdW1CaIfwCeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.96.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.96.2.tgz", + "integrity": "sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.96.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", + "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.340", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz", + "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-toolkit": { + "version": "1.45.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.45.1.tgz", + "integrity": "sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "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" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.8.0.tgz", + "integrity": "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.72.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.1.tgz", + "integrity": "sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.5.tgz", + "integrity": "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==", + "license": "MIT", + "peer": true + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.3.tgz", + "integrity": "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.3.tgz", + "integrity": "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.2", + "react-router": "6.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/recharts": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.8.1.tgz", + "integrity": "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "^1.9.0 || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "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 + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/lidmas+/frontend/package.json b/lidmas+/frontend/package.json new file mode 100644 index 0000000..0d2dbd5 --- /dev/null +++ b/lidmas+/frontend/package.json @@ -0,0 +1,38 @@ +{ + "name": "lidmas-frontend", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host 127.0.0.1 --port 5173 --strictPort", + "lidmas": "npm run dev", + "dev:chrome": "sh -c 'sleep 2; open -na \"Google Chrome\" --args --app=http://127.0.0.1:5173 --new-window >/dev/null 2>&1 &' && npm run dev", + "lidmas:stack": "sh -c 'cd .. && ./run_lidmas.sh'", + "build": "tsc --noEmit && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@tanstack/react-query": "^5.59.20", + "lucide-react": "^1.8.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.72.1", + "react-router-dom": "^6.30.1", + "recharts": "^3.8.1", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.2.2", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@vitejs/plugin-react": "^4.7.0", + "autoprefixer": "^10.5.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "postcss": "^8.5.10", + "tailwind-merge": "^3.5.0", + "tailwindcss": "^4.2.2", + "typescript": "^5.6.3", + "vite": "^5.4.10" + } +} diff --git a/lidmas+/frontend/postcss.config.js b/lidmas+/frontend/postcss.config.js new file mode 100644 index 0000000..a7f73a2 --- /dev/null +++ b/lidmas+/frontend/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +} diff --git a/lidmas+/frontend/src/api/client.ts b/lidmas+/frontend/src/api/client.ts new file mode 100644 index 0000000..abf9bd7 --- /dev/null +++ b/lidmas+/frontend/src/api/client.ts @@ -0,0 +1,53 @@ +import { clearAuthSession, loadAuthSession } from "../auth/session"; + +const DEFAULT_BASE = "http://127.0.0.1:8080/api/v1"; + +export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? DEFAULT_BASE; + +export interface ApiErrorPayload { + error?: string; +} + +export class ApiError extends Error { + status: number; + + constructor(message: string, status: number) { + super(message); + this.name = "ApiError"; + this.status = status; + } +} + +export async function apiFetch(path: string, init?: RequestInit): Promise { + const url = `${API_BASE_URL}${path}`; + const session = loadAuthSession(); + const headers = new Headers(init?.headers ?? undefined); + headers.set("content-type", "application/json"); + let sentAuthHeader = false; + if (session?.token && session?.token_type && !headers.has("authorization")) { + headers.set("authorization", `${session.token_type} ${session.token}`); + sentAuthHeader = true; + } else if (headers.has("authorization")) { + sentAuthHeader = true; + } + const response = await fetch(url, { + ...init, + headers, + }); + if (!response.ok) { + let message = `Request failed (${response.status})`; + try { + const payload = (await response.json()) as ApiErrorPayload; + if (payload.error) { + message = payload.error; + } + } catch { + // keep default message + } + if (response.status === 401 && sentAuthHeader) { + clearAuthSession(); + } + throw new ApiError(message, response.status); + } + return (await response.json()) as T; +} diff --git a/lidmas+/frontend/src/api/hooks.ts b/lidmas+/frontend/src/api/hooks.ts new file mode 100644 index 0000000..5192e57 --- /dev/null +++ b/lidmas+/frontend/src/api/hooks.ts @@ -0,0 +1,420 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; + +import { apiFetch } from "./client"; +import type { + AuthSessionResponse, + AuthSignInRequest, + AuthSignOutResponse, + AuthSignUpRequest, + CompleteHardwareSessionResponse, + CreateHardwareSessionRequest, + CreateHardwareSessionResponse, + AuthUserProfile, + CreateJobRequest, + CreateProviderRequest, + CreateRunRequest, + HealthResponse, + HardwareApiSchemaResponse, + HardwareSession, + IngestHardwareFramesRequest, + IngestHardwareFramesResponse, + IntegrationSession, + IntegrationSessionLogsResponse, + Job, + Paper04Manifest, + Provider, + RunPaper04Request, + RunPaper04Response, + Run, + RunTelemetry, + SetIbmApiKeyRequest, + SetIbmApiKeyResponse, + VendorCalibrationRefreshResponse, + VendorCalibrationsCatalogResponse, + StartIntegrationSessionRequest, + StopIntegrationSessionResponse, + SystemLogScanRequest, + SystemLogScanResponse, + UpsertRunTelemetryRequest, + ValidationReport, +} from "./types"; + +interface QueryHookOptions { + enabled?: boolean; + refetchInterval?: number; +} + +interface HealthQueryHookOptions extends QueryHookOptions {} + +interface PollingQueryHookOptions extends QueryHookOptions {} + +interface RunTelemetryQueryHookOptions extends QueryHookOptions { + scientificMode?: boolean; +} + +export function useHealth(options?: HealthQueryHookOptions) { + return useQuery({ + queryKey: ["health"], + queryFn: () => apiFetch("/health"), + refetchInterval: options?.refetchInterval ?? 5000, + enabled: options?.enabled ?? true, + }); +} + +export function useSignUp() { + return useMutation({ + mutationFn: (payload: AuthSignUpRequest) => + apiFetch("/auth/signup", { + method: "POST", + body: JSON.stringify(payload), + }), + }); +} + +export function useSignIn() { + return useMutation({ + mutationFn: (payload: AuthSignInRequest) => + apiFetch("/auth/signin", { + method: "POST", + body: JSON.stringify(payload), + }), + }); +} + +export function useAuthMe(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["auth-me"], + queryFn: () => apiFetch("/auth/me"), + enabled: options?.enabled ?? true, + retry: false, + }); +} + +export function useSignOut() { + return useMutation({ + mutationFn: () => + apiFetch("/auth/signout", { + method: "POST", + body: JSON.stringify({}), + }), + }); +} + +export function useProviders(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["providers"], + queryFn: () => apiFetch("/providers"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval, + }); +} + +export function useCreateProvider() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: CreateProviderRequest) => + apiFetch("/providers", { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["providers"] }); + }, + }); +} + +export function useJobs(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["jobs"], + queryFn: () => apiFetch("/jobs"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval, + }); +} + +export function useCreateJob() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: CreateJobRequest) => + apiFetch("/jobs", { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["jobs"] }); + }, + }); +} + +export function useRuns(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["runs"], + queryFn: () => apiFetch("/runs"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval, + }); +} + +export function useCreateRun() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: CreateRunRequest) => + apiFetch("/runs", { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["runs"] }); + }, + }); +} + +export function useRunTelemetry(runId: string | null, options?: RunTelemetryQueryHookOptions) { + const scientificParam = options?.scientificMode ? "?scientific=true" : ""; + return useQuery({ + queryKey: ["run-telemetry", runId, scientificParam], + queryFn: () => apiFetch(`/runs/${runId}/telemetry${scientificParam}`), + enabled: Boolean(runId) && (options?.enabled ?? true), + refetchInterval: options?.refetchInterval, + }); +} + +export function useUpsertRunTelemetry(runId: string | null) { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: UpsertRunTelemetryRequest) => { + if (!runId) { + throw new Error("runId is required"); + } + return apiFetch(`/runs/${runId}/telemetry`, { + method: "POST", + body: JSON.stringify(payload), + }); + }, + onSuccess: async (_, __, ___) => { + if (runId) { + await qc.invalidateQueries({ queryKey: ["run-telemetry", runId] }); + } + await qc.invalidateQueries({ queryKey: ["runs"] }); + }, + }); +} + +export function useHardwareSchema(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["hardware-schema"], + queryFn: () => apiFetch("/hardware/schema"), + enabled: options?.enabled ?? true, + }); +} + +export function useHardwareSessions(options?: PollingQueryHookOptions) { + return useQuery({ + queryKey: ["hardware-sessions"], + queryFn: () => apiFetch("/hardware/sessions"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval ?? 3000, + }); +} + +export function useCreateHardwareSession() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: CreateHardwareSessionRequest) => + apiFetch("/hardware/sessions", { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["hardware-sessions"] }); + await qc.invalidateQueries({ queryKey: ["runs"] }); + }, + }); +} + +interface IngestHardwareFramesMutationPayload { + sessionId: string; + payload: IngestHardwareFramesRequest; +} + +export function useIngestHardwareFrames() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: ({ sessionId, payload }: IngestHardwareFramesMutationPayload) => + apiFetch(`/hardware/sessions/${sessionId}/frames`, { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async (response) => { + await qc.invalidateQueries({ queryKey: ["hardware-sessions"] }); + await qc.invalidateQueries({ queryKey: ["run-telemetry", response.telemetry.run_id] }); + await qc.invalidateQueries({ queryKey: ["runs"] }); + }, + }); +} + +export function useCompleteHardwareSession() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (sessionId: string) => + apiFetch(`/hardware/sessions/${sessionId}/complete`, { + method: "POST", + body: JSON.stringify({}), + }), + onSuccess: async (response) => { + await qc.invalidateQueries({ queryKey: ["hardware-sessions"] }); + await qc.invalidateQueries({ queryKey: ["runs"] }); + if (response.telemetry?.run_id) { + await qc.invalidateQueries({ queryKey: ["run-telemetry", response.telemetry.run_id] }); + } + }, + }); +} + +export interface ValidatePayload { + providerId: string; + dataset_label: string; + request_lines: number; + response_lines: number; + request_parse_errors: number; + response_parse_errors: number; + decoder_name_mismatch_count: number; + warning_no_syndrome_count?: number; +} + +export function useValidateProviderOutput() { + return useMutation({ + mutationFn: (payload: ValidatePayload) => { + const { providerId, ...body } = payload; + return apiFetch(`/providers/${providerId}/validate`, { + method: "POST", + body: JSON.stringify(body), + }); + }, + }); +} + +export function useSystemLogScan() { + return useMutation({ + mutationFn: (payload: SystemLogScanRequest) => + apiFetch("/system/logscan", { + method: "POST", + body: JSON.stringify(payload), + }), + }); +} + +export function useSetIbmApiKey() { + return useMutation({ + mutationFn: (payload: SetIbmApiKeyRequest) => + apiFetch("/system/credentials/ibm", { + method: "POST", + body: JSON.stringify(payload), + }), + }); +} + +export function useVendorCalibrations(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["vendor-calibrations"], + queryFn: () => apiFetch("/system/calibrations"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval, + retry: false, + }); +} + +export function useRefreshVendorCalibrations() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: () => + apiFetch("/system/calibrations/refresh", { + method: "POST", + body: JSON.stringify({}), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["vendor-calibrations"] }); + }, + }); +} + +export function usePaper04Manifest(options?: QueryHookOptions) { + return useQuery({ + queryKey: ["paper-04-manifest"], + queryFn: () => apiFetch("/system/paper_04/manifest"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval, + retry: false, + }); +} + +export function useRunPaper04() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: RunPaper04Request) => + apiFetch("/system/paper_04/run", { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["paper-04-manifest"] }); + await qc.invalidateQueries({ queryKey: ["runs"] }); + await qc.invalidateQueries({ queryKey: ["integration-sessions"] }); + }, + }); +} + +export function useIntegrationSessions(options?: PollingQueryHookOptions) { + return useQuery({ + queryKey: ["integration-sessions"], + queryFn: () => apiFetch("/integrations/sessions"), + enabled: options?.enabled ?? true, + refetchInterval: options?.refetchInterval ?? 3000, + retry: false, + }); +} + +export function useCreateIntegrationSession() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (payload: StartIntegrationSessionRequest) => + apiFetch("/integrations/sessions", { + method: "POST", + body: JSON.stringify(payload), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["integration-sessions"] }); + }, + }); +} + +export function useStopIntegrationSession() { + const qc = useQueryClient(); + return useMutation({ + mutationFn: (sessionId: string) => + apiFetch(`/integrations/sessions/${sessionId}/stop`, { + method: "POST", + body: JSON.stringify({}), + }), + onSuccess: async () => { + await qc.invalidateQueries({ queryKey: ["integration-sessions"] }); + }, + }); +} + +export function useIntegrationSessionLogs( + sessionId: string | null, + tail: number, + options?: PollingQueryHookOptions, +) { + return useQuery({ + queryKey: ["integration-session-logs", sessionId, tail], + queryFn: () => + apiFetch( + `/integrations/sessions/${sessionId}/logs?tail=${encodeURIComponent(tail)}`, + ), + enabled: Boolean(sessionId) && (options?.enabled ?? true), + refetchInterval: options?.refetchInterval ?? 2000, + retry: false, + }); +} diff --git a/lidmas+/frontend/src/api/types.ts b/lidmas+/frontend/src/api/types.ts new file mode 100644 index 0000000..00602ce --- /dev/null +++ b/lidmas+/frontend/src/api/types.ts @@ -0,0 +1,607 @@ +export interface HealthResponse { + status: string; + version: string; + started_at: string; + uptime_seconds: number; +} + +export interface AuthUserProfile { + id: string; + full_name: string; + email: string; + created_at: string; +} + +export interface AuthSignUpRequest { + full_name: string; + email: string; + password: string; +} + +export interface AuthSignInRequest { + email: string; + password: string; +} + +export interface AuthSessionResponse { + token: string; + token_type: string; + expires_at: string; + user: AuthUserProfile; +} + +export interface AuthSignOutResponse { + signed_out: boolean; + message: string; +} + +export type ProviderKind = + | "photonic" + | "superconducting" + | "trapped_ion" + | "simulated" + | "other"; + +export type ProviderStatus = "ready" | "degraded" | "offline"; + +export interface Provider { + id: string; + name: string; + status: ProviderStatus; + kind: ProviderKind; + hardware_kind: ProviderKind; + contact_email?: string | null; + supported_formats: string[]; + supports_scientific: boolean; + supports_benchmark: boolean; + supports_replay: boolean; + supports_live: boolean; + last_seen?: string | null; + readiness_note?: string | null; + notes?: string | null; + created_at: string; + updated_at: string; +} + +export interface CreateProviderRequest { + name: string; + kind: ProviderKind; + status?: ProviderStatus; + contact_email?: string; + supported_formats: string[]; + supports_scientific?: boolean; + supports_benchmark?: boolean; + supports_replay?: boolean; + supports_live?: boolean; + readiness_note?: string; + notes?: string; +} + +export type JobStatus = "queued" | "running" | "completed" | "failed" | "cancelled"; + +export interface Job { + id: string; + provider_id: string; + dataset_label: string; + decoders: string[]; + priority: number; + status: JobStatus; + message?: string | null; + created_at: string; + updated_at: string; + started_at?: string | null; + completed_at?: string | null; +} + +export interface CreateJobRequest { + provider_id: string; + dataset_label: string; + decoders: string[]; + priority?: number; +} + +export type RunStatus = "created" | "running" | "finished" | "failed" | "cancelled"; + +export interface RunArtifact { + name: string; + kind: string; + path: string; + sha256?: string | null; + created_at: string; +} + +export interface RunDecoderRanking { + decoder: string; + logical_error_rate: number; + avg_flips: number; + residual_nonzero_rate: number; + correction_efficiency: number; +} + +export interface DecoderExactTelemetry { + decoder: string; + trials: number; + logical_failures: number; + encoder_state?: string | null; +} + +export interface RunMetrics { + avg_flip_count?: number | null; + nonempty_flip_rate?: number | null; + syndrome_satisfaction_rate?: number | null; + residual_nonzero_rate?: number | null; + warning_rate?: number | null; + physical_error_rate?: number | null; + baseline_logical_error_rate?: number | null; + logical_error_rate?: number | null; + logical_failures?: number | null; + logical_trials?: number | null; + physical_error_events?: number | null; + physical_error_opportunities?: number | null; + request_line_count?: number | null; + response_line_count?: number | null; + rounds?: number | null; + stabilizer_count?: number | null; + syndrome_opportunities?: number | null; + residual_syndrome_events?: number | null; + expanded_shot_count?: number | null; + decoder_exact_metrics?: DecoderExactTelemetry[] | null; + ler_per_gate_triggered?: boolean | null; + ler_per_gate_passed?: boolean | null; + logical_error_rate_source?: string | null; + scientific_validation_ready?: boolean | null; + best_decoder?: string | null; + best_encoder_state?: string | null; + decoder_rankings?: RunDecoderRanking[] | null; +} + +export interface Run { + id: string; + job_id?: string | null; + workflow_id?: string | null; + provider_id: string; + dataset_label: string; + decoders: string[]; + status: RunStatus; + message?: string | null; + artifacts: RunArtifact[]; + metrics?: RunMetrics | null; + created_at: string; + updated_at: string; +} + +export interface CreateRunRequest { + job_id?: string; + workflow_id?: string; + provider_id?: string; + dataset_label?: string; + decoders?: string[]; +} + +export interface ValidationReport { + provider_id: string; + dataset_label: string; + line_coverage_ok: boolean; + parse_integrity_ok: boolean; + decoder_name_integrity_ok: boolean; + warning_rate?: number | null; + overall_ok: boolean; + checks: string[]; + checked_at: string; +} + +export interface NoiseSample { + index: number; + physical_error_rate: number; + displacement_sigma: number; + photon_loss_rate: number; +} + +export interface SyndromeSample { + round: number; + stabilizer: string; + value: number; + is_triggered: boolean; +} + +export interface GkpOscillatorStateSample { + round: number; + mode: string; + q: number; + p: number; + variance?: number | null; + energy?: number | null; + flagged?: boolean; +} + +export interface DecoderIntervention { + decoder: string; + round: number; + flips: number; + residual_weight: number; +} + +export interface RunTelemetry { + run_id: string; + // Legacy overloaded counter; scientific mode should prefer explicit line/shot counters below. + request_count: number; + request_line_count?: number | null; + response_line_count?: number | null; + response_ratio?: number | null; + expanded_shot_count?: number | null; + rounds: number; + stabilizer_count: number; + syndrome_opportunities?: number | null; + decoder_name?: string | null; + logical_failures?: number | null; + logical_trials?: number | null; + logical_error_rate?: number | null; + physical_error_events?: number | null; + physical_error_opportunities?: number | null; + physical_error_rate?: number | null; + residual_syndrome_events?: number | null; + residual_syndrome_rate?: number | null; + warning_rate?: number | null; + noise_samples: NoiseSample[]; + syndrome_samples: SyndromeSample[]; + decoder_exact_metrics?: DecoderExactTelemetry[] | null; + gkp_oscillator_states?: GkpOscillatorStateSample[]; + decoder_interventions: DecoderIntervention[]; + updated_at: string; +} + +export interface UpsertRunTelemetryRequest { + request_count?: number; + request_line_count?: number; + response_line_count?: number; + response_ratio?: number; + expanded_shot_count?: number; + rounds?: number; + stabilizer_count?: number; + syndrome_opportunities?: number; + decoder_name?: string; + logical_failures?: number; + logical_trials?: number; + logical_error_rate?: number; + physical_error_events?: number; + physical_error_opportunities?: number; + physical_error_rate?: number; + residual_syndrome_events?: number; + residual_syndrome_rate?: number; + warning_rate?: number; + noise_samples: NoiseSample[]; + syndrome_samples: SyndromeSample[]; + decoder_exact_metrics?: DecoderExactTelemetry[]; + gkp_oscillator_states?: GkpOscillatorStateSample[]; + decoder_interventions: DecoderIntervention[]; +} + +export type HardwareSourceMode = "live" | "replay"; +export type HardwareSessionStatus = "active" | "completed" | "failed" | "cancelled"; + +export interface HardwareSession { + id: string; + run_id: string; + provider_id: string; + dataset_label: string; + source_name: string; + source_mode: HardwareSourceMode; + schema_version: string; + decoders: string[]; + status: HardwareSessionStatus; + frame_count: number; + started_at: string; + updated_at: string; + last_frame_at?: string | null; + completed_at?: string | null; + last_error?: string | null; +} + +export interface CreateHardwareSessionRequest { + provider_id: string; + dataset_label: string; + decoders: string[]; + source_name: string; + source_mode?: HardwareSourceMode; + schema_version?: string; +} + +export interface CreateHardwareSessionResponse { + session: HardwareSession; + run: Run; + frame_ingest_path: string; + complete_path: string; +} + +export interface HardwareNoiseSampleInput { + index?: number; + physical_error_rate: number; + displacement_sigma: number; + photon_loss_rate: number; +} + +export interface HardwareFrameInput { + frame_index: number; + timestamp?: string; + source?: string; + backend_name?: string; + warning_rate?: number; + noise_sample: HardwareNoiseSampleInput; + syndrome_samples: SyndromeSample[]; + decoder_interventions: DecoderIntervention[]; +} + +export interface IngestHardwareFramesRequest { + frames: HardwareFrameInput[]; +} + +export interface IngestHardwareFramesResponse { + session: HardwareSession; + telemetry: RunTelemetry; + ingested_frames: number; +} + +export interface CompleteHardwareSessionResponse { + session: HardwareSession; + run: Run; + telemetry?: RunTelemetry | null; +} + +export interface HardwareApiSchemaResponse { + schema_version: string; + frame_format: string; + notes: string[]; + create_session_request_example: Record; + frame_request_example: Record; +} + +export type IntegrationProvider = + | "ibm" + | "ankaa" + | "xanadu" + | "pennylane" + | "qiskit" + | "cirq"; +export type IntegrationMode = "live" | "replay_static"; +export type IntegrationAdapterId = + | "ibm_superconducting_live" + | "ankaa_superconducting_replay" + | "xanadu_gkp_remote_replay" + | "pennylane_surface_replay" + | "qiskit_surface_replay" + | "cirq_surface_replay"; +export type IntegrationSessionStatus = "starting" | "running" | "finished" | "failed" | "cancelled"; +export type IntegrationLogStream = "stdout" | "stderr" | "system"; + +export interface IntegrationSessionConfig { + mode?: "scientific" | "benchmark" | "replay"; + provider_scope?: string; + time_range?: string; + run_source?: string; + compare_decoders?: string[]; + backend_name?: string; + ibm_live_source_mode?: "metadata" | "qpu"; + ibm_instance?: string; + ibm_shots?: number; + poll_interval?: number; + max_polls?: number; + input_path?: string; + max_rounds?: number; + remote?: string; + remote_repo?: string; + remote_python?: string; + remote_input_root?: string; + neural_model_path?: string; + skip_replay?: boolean; + simulator_code_family?: "surface" | "gkp"; + simulator_shots?: number; + simulator_distance?: number; + simulator_rounds?: number; + simulator_error_rate?: number; + simulator_sigma?: number; + simulator_seed?: number; + circuit_name?: string; + circuit_qasm?: string; + circuit_qubits?: number; + circuit_depth?: number; + circuit_gate_count?: number; + circuit_hardware_target?: "superconducting" | "trapped_ion" | "photonic"; + circuit_detector_model?: "threshold" | "pnr_approx"; + circuit_noise_config?: string; + circuit_compile_artifact?: string; + circuit_calibration_snapshot?: string; + circuit_gate_plan?: string; +} + +export interface IntegrationSession { + id: string; + run_id: string; + provider: IntegrationProvider; + mode: IntegrationMode; + adapter_id: IntegrationAdapterId; + status: IntegrationSessionStatus; + config: IntegrationSessionConfig; + started_at: string; + updated_at: string; + ended_at?: string | null; + exit_code?: number | null; + last_error?: string | null; +} + +export interface StartIntegrationSessionRequest { + run_id: string; + adapter_id: IntegrationAdapterId; + config?: IntegrationSessionConfig; +} + +export interface StopIntegrationSessionResponse { + session: IntegrationSession; + stopped: boolean; + message: string; +} + +export interface IntegrationSessionLogLine { + timestamp: string; + stream: IntegrationLogStream; + line: string; +} + +export interface IntegrationSessionLogsResponse { + session_id: string; + total_lines: number; + has_more: boolean; + lines: IntegrationSessionLogLine[]; +} + +export type LogSeverity = "critical" | "high" | "medium" | "low" | "info"; +export type LogScanField = "any" | "message" | "source" | "level"; +export type LogScanVerdict = "pass" | "warn" | "critical"; + +export interface SystemLogEntry { + timestamp?: string | null; + level?: string | null; + message: string; + source?: string | null; +} + +export interface SystemLogScanRule { + id: string; + title: string; + pattern: string; + severity: LogSeverity; + confidence?: number; + field?: LogScanField; + tags?: string[]; + recommendation?: string; +} + +export interface SystemLogSuppression { + pattern: string; + field?: LogScanField; +} + +export interface SystemLogScanRequest { + logs: SystemLogEntry[]; + custom_rules?: SystemLogScanRule[]; + suppressions?: SystemLogSuppression[]; + max_findings?: number; +} + +export interface SystemLogScanFinding { + rule_id: string; + rule_origin: string; + title: string; + severity: LogSeverity; + confidence: number; + line_index: number; + timestamp?: string | null; + level?: string | null; + source?: string | null; + message: string; + tags: string[]; + recommendation?: string | null; +} + +export interface SystemLogScanSummary { + scanned_entries: number; + matched_entries: number; + suppressed_matches: number; + critical_count: number; + high_count: number; + medium_count: number; + low_count: number; + info_count: number; + risk_score: number; + verdict: LogScanVerdict; +} + +export interface SystemLogScanResponse { + scan_id: string; + summary: SystemLogScanSummary; + findings: SystemLogScanFinding[]; + top_recommendations: string[]; + generated_at: string; +} + +export interface SetIbmApiKeyRequest { + api_key: string; +} + +export interface SetIbmApiKeyResponse { + stored: boolean; + message: string; + updated_at: string; +} + +export interface VendorCalibrationSnapshot { + id: string; + label: string; + vendor: string; + hardware_target: string; + backend: string; + captured_at: string; + source: string; + metrics: Record; +} + +export interface VendorCalibrationsCatalogResponse { + schema_version?: string; + generated_at?: string; + refresh_mode?: string; + snapshots?: VendorCalibrationSnapshot[]; + notes?: string[]; +} + +export interface VendorCalibrationRefreshResponse { + ok: boolean; + command: string; + duration_ms: number; + exit_code?: number | null; + stdout_tail: string[]; + stderr_tail: string[]; + catalog_path: string; + refreshed_at: string; + catalog?: VendorCalibrationsCatalogResponse; +} + +export interface Paper04ArtifactDigest { + path: string; + exists: boolean; + size_bytes?: number | null; + sha256?: string | null; +} + +export interface Paper04Manifest { + generated_at: string; + results_root: string; + artifact_count: number; + manifest_hash: string; + artifacts: Paper04ArtifactDigest[]; +} + +export interface Paper04ParityResult { + expected_manifest_hash: string; + actual_manifest_hash: string; + match_exact: boolean; +} + +export interface RunPaper04Request { + strict_three_stack?: boolean; + enable_param_sweeps?: boolean; + timeout_seconds?: number; + env_overrides?: Record; + compare_with_manifest_hash?: string; +} + +export interface RunPaper04Response { + ok: boolean; + status: string; + command: string; + duration_ms: number; + timeout_seconds: number; + exit_code?: number | null; + stdout_tail: string[]; + stderr_tail: string[]; + manifest: Paper04Manifest; + parity?: Paper04ParityResult | null; +} diff --git a/lidmas+/frontend/src/assets/partner-logos/ankaa.svg b/lidmas+/frontend/src/assets/partner-logos/ankaa.svg new file mode 100644 index 0000000..69f2161 --- /dev/null +++ b/lidmas+/frontend/src/assets/partner-logos/ankaa.svg @@ -0,0 +1,7 @@ + diff --git a/lidmas+/frontend/src/assets/partner-logos/cirq.svg b/lidmas+/frontend/src/assets/partner-logos/cirq.svg new file mode 100644 index 0000000..0f755a9 --- /dev/null +++ b/lidmas+/frontend/src/assets/partner-logos/cirq.svg @@ -0,0 +1,24 @@ + + + + + + Cirq logo + + + + + + + + + + + + + diff --git a/lidmas+/frontend/src/assets/partner-logos/ibm.svg b/lidmas+/frontend/src/assets/partner-logos/ibm.svg new file mode 100644 index 0000000..209c96e --- /dev/null +++ b/lidmas+/frontend/src/assets/partner-logos/ibm.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/lidmas+/frontend/src/assets/partner-logos/pennylane.svg b/lidmas+/frontend/src/assets/partner-logos/pennylane.svg new file mode 100644 index 0000000..2ad508a --- /dev/null +++ b/lidmas+/frontend/src/assets/partner-logos/pennylane.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lidmas+/frontend/src/assets/partner-logos/qiskit.png b/lidmas+/frontend/src/assets/partner-logos/qiskit.png new file mode 100644 index 0000000..7362206 Binary files /dev/null and b/lidmas+/frontend/src/assets/partner-logos/qiskit.png differ diff --git a/lidmas+/frontend/src/assets/partner-logos/qiskit.svg b/lidmas+/frontend/src/assets/partner-logos/qiskit.svg new file mode 100644 index 0000000..5f665fd --- /dev/null +++ b/lidmas+/frontend/src/assets/partner-logos/qiskit.svg @@ -0,0 +1,99 @@ + diff --git a/lidmas+/frontend/src/assets/partner-logos/xanadu.png b/lidmas+/frontend/src/assets/partner-logos/xanadu.png new file mode 100644 index 0000000..0f36078 Binary files /dev/null and b/lidmas+/frontend/src/assets/partner-logos/xanadu.png differ diff --git a/lidmas+/frontend/src/auth/session.ts b/lidmas+/frontend/src/auth/session.ts new file mode 100644 index 0000000..29ffe5b --- /dev/null +++ b/lidmas+/frontend/src/auth/session.ts @@ -0,0 +1,70 @@ +import type { AuthSessionResponse, AuthUserProfile } from "../api/types"; + +export interface AuthSession extends AuthSessionResponse { + signed_in_at: string; +} + +const SESSION_STORAGE_KEY = "lidmas.auth.session"; +export const AUTH_UPDATED_EVENT = "lidmas:auth-updated"; + +function readJson(raw: string | null): T | null { + if (!raw) { + return null; + } + try { + return JSON.parse(raw) as T; + } catch { + return null; + } +} + +function isSessionExpired(expiresAt: string): boolean { + const timestamp = new Date(expiresAt).getTime(); + if (!Number.isFinite(timestamp)) { + return true; + } + return Date.now() >= timestamp; +} + +export function loadAuthSession(): AuthSession | null { + if (typeof window === "undefined") { + return null; + } + const parsed = readJson(window.localStorage.getItem(SESSION_STORAGE_KEY)); + if (!parsed?.token || !parsed?.token_type || !parsed?.expires_at || !parsed?.user) { + return null; + } + if (isSessionExpired(parsed.expires_at)) { + window.localStorage.removeItem(SESSION_STORAGE_KEY); + return null; + } + return parsed; +} + +export function saveAuthSession(payload: AuthSessionResponse): AuthSession { + const session: AuthSession = { + ...payload, + signed_in_at: new Date().toISOString(), + }; + if (typeof window !== "undefined") { + window.localStorage.setItem(SESSION_STORAGE_KEY, JSON.stringify(session)); + window.dispatchEvent(new Event(AUTH_UPDATED_EVENT)); + } + return session; +} + +export function clearAuthSession(): void { + if (typeof window === "undefined") { + return; + } + window.localStorage.removeItem(SESSION_STORAGE_KEY); + window.dispatchEvent(new Event(AUTH_UPDATED_EVENT)); +} + +export function sessionUserDisplayName(user: AuthUserProfile | null | undefined): string { + if (!user) { + return "Guest"; + } + const value = user.full_name.trim(); + return value || "Guest"; +} diff --git a/lidmas+/frontend/src/components/ExampleDashboard.tsx b/lidmas+/frontend/src/components/ExampleDashboard.tsx new file mode 100644 index 0000000..dd55644 --- /dev/null +++ b/lidmas+/frontend/src/components/ExampleDashboard.tsx @@ -0,0 +1,209 @@ +/** + * EXAMPLE: Modern component using Shadcn/ui + Tailwind + * Shows best practices for the research UI setup + */ + +import { useState } from "react" +import { useForm } from "react-hook-form" +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Badge } from "@/components/ui/badge" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Settings, AlertCircle, CheckCircle2 } from "lucide-react" + +interface FormData { + name: string + email: string +} + +export function ExampleDashboard() { + const [items] = useState([ + { id: 1, name: "Provider A", status: "active", count: 42 }, + { id: 2, name: "Provider B", status: "warning", count: 8 }, + { id: 3, name: "Provider C", status: "error", count: 0 }, + ]) + + const { register, handleSubmit } = useForm() + + const onSubmit = (data: FormData) => { + console.log("Form data:", data) + } + + const getStatusVariant = (status: string) => { + switch (status) { + case "active": + return "success" + case "warning": + return "warning" + case "error": + return "destructive" + default: + return "default" + } + } + + const getStatusIcon = (status: string) => { + if (status === "active") return + if (status === "error") return + return null + } + + return ( +
+ {/* Header Section */} +
+
+

Dashboard Example

+

+ Shows best practices using Shadcn/ui + Tailwind +

+
+ +
+ + {/* Cards Grid */} +
+ {[ + { label: "Active Providers", value: "12", variant: "success" as const }, + { label: "Jobs Queued", value: "148", variant: "warning" as const }, + { label: "Storage Used", value: "2.4 GB", variant: "default" as const }, + ].map((metric) => ( + + + {metric.label} + + +

{metric.value}

+ + {metric.variant} + +
+
+ ))} +
+ + {/* Form Card */} + + + Configuration Form + + Example using React Hook Form + Tailwind styling + + + +
+
+
+ + +
+
+ + +
+
+ +
+
+
+ + {/* Table Card */} + + + Providers Table + + Example data table using Shadcn/ui Table component + + + +
+ + + + Name + Status + Job Count + + + + {items.map((item) => ( + + {item.name} + +
+ {getStatusIcon(item.status)} + + {item.status} + +
+
+ + {item.count} + +
+ ))} +
+
+
+
+
+ + {/* Tailwind Utility Classes Examples */} + + + Tailwind Utilities Cheat Sheet + + +
+ Spacing: +

+ p-4 (padding), m-2 (margin), gap-6 (flex gap) +

+
+
+ Colors: +

+ text-primary, bg-secondary, border-border +

+
+
+ Responsive: +

+ md:grid-cols-3 (medium screens), lg:text-lg (large screens) +

+
+
+ Effects: +

+ shadow-lg, rounded-lg, hover:bg-accent, transition-colors +

+
+
+
+
+ ) +} diff --git a/lidmas+/frontend/src/components/ui/badge.tsx b/lidmas+/frontend/src/components/ui/badge.tsx new file mode 100644 index 0000000..530b092 --- /dev/null +++ b/lidmas+/frontend/src/components/ui/badge.tsx @@ -0,0 +1,38 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + success: "border-transparent bg-green-100 text-green-700 hover:bg-green-200", + warning: "border-transparent bg-orange-100 text-orange-700 hover:bg-orange-200", + info: "border-transparent bg-blue-100 text-blue-700 hover:bg-blue-200", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/lidmas+/frontend/src/components/ui/button.tsx b/lidmas+/frontend/src/components/ui/button.tsx new file mode 100644 index 0000000..7ee66fd --- /dev/null +++ b/lidmas+/frontend/src/components/ui/button.tsx @@ -0,0 +1,44 @@ +import * as React from "react" +import { cn } from "@/lib/utils" + +export interface ButtonProps + extends React.ButtonHTMLAttributes { + variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" + size?: "default" | "sm" | "lg" | "icon" +} + +const Button = React.forwardRef( + ({ className, variant = "default", size = "default", ...props }, ref) => { + return ( + + + +
+
+ +
+ {tabs.map((tab) => ( + + ))} +
+ + {activeTab === "active" ? ( + <> + {filteredAlerts.map((alert) => ( +
+
+
+
{alert.title}
+
+ {alert.severity} + {statusText(alert.status)} + {alert.category} +
+
+
+ {formatRelativeAge(alert.triggeredAtMs)} + {alert.source} +
+
+
{alert.summary}
+
+ {alert.impact} + {formatUtcDateTime(alert.triggeredAtMs)} +
+
{alert.suggestedAction}
+
+ + + + + + +
+
+ ))} + + {hasOnlyHealthyInfo ? ( +
+ No Active Incidents +

Current runtime appears healthy under configured alert thresholds.

+
+ ) : null} + + {hasApiWarning ? ( +
+ API Warning +

One or more API resources failed to load. Alert status may be incomplete.

+
+ ) : null} + + {!hasApiWarning && filteredAlerts.length === 0 ? ( +
+ {isApiLoading ? "Loading Incident Feed" : "No Alerts for Current Filters"} +

+ {isApiLoading + ? "Collecting provider, job, and run signals from the API." + : "Try broadening severity/status filters or clear search terms."} +

+
+ ) : null} + + ) : null} + + {activeTab === "history" ? ( + <> +
+ Events recorded: {timeline.length} + Resolved alerts: {resolvedCount} + Suppressed alerts: {suppressedCount} +
+
+ {timeline.map((event) => ( +
+
{formatUtcDateTime(event.atMs)}
+
+ {event.action} +

+ Alert: {event.alertId} · {event.note} +

+
+
+ ))} + {timeline.length === 0 ? ( +
+ No Timeline Events Yet +

Alert actions will appear here once incidents are acknowledged or resolved.

+
+ ) : null} +
+ + ) : null} + + {activeTab === "rules" ? ( + <> +
+ setRulesSearch(event.target.value)} + /> + {rules.filter((rule) => rule.enabled).length} enabled +
+ {rules + .filter((rule) => { + const token = rulesSearch.trim().toLowerCase(); + if (token.length === 0) { + return true; + } + return `${rule.name} ${rule.condition}`.toLowerCase().includes(token); + }) + .map((rule) => ( +
+
+
{rule.name}
+
{rule.condition}
+
+ {rule.severity} + channel: {rule.channel} +
+
+
+ + +
+
+ ))} + + ) : null} + + {activeTab === "notifications" ? ( +
+
+
+
Delivery Channels
+
Choose which channels receive incident notifications.
+
+
+ {(["pagerduty", "slack", "email", "sms"] as const).map((channel) => ( + + ))} +
+
+
+
+
Quiet Hours
+
Suppress non-critical pages during scheduled quiet periods.
+
+
+ + + setNotificationPreferences((previous) => ({ + ...previous, + quietHoursStart: event.target.value, + })) + } + /> + + setNotificationPreferences((previous) => ({ + ...previous, + quietHoursEnd: event.target.value, + })) + } + /> +
+
+
+
+
Digest Cadence
+
Batch low-priority updates to reduce noise during stable periods.
+
+
+ +
+
+
+ ) : null} + + ); +} diff --git a/lidmas+/frontend/src/ui/pages/DecoderDashboard.tsx b/lidmas+/frontend/src/ui/pages/DecoderDashboard.tsx new file mode 100644 index 0000000..221d725 --- /dev/null +++ b/lidmas+/frontend/src/ui/pages/DecoderDashboard.tsx @@ -0,0 +1,4678 @@ +import { useEffect, useMemo, useRef, useState } from "react"; +import { Activity, ChevronDown, ChevronUp, Power, ShieldCheck } from "lucide-react"; +import { createPortal } from "react-dom"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { + Area, + AreaChart, + CartesianGrid, + Line, + LineChart, + ReferenceArea, + ReferenceDot, + ReferenceLine, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; + +import { ApiError } from "../../api/client"; +import { + useCreateIntegrationSession, + useCreateRun, + useHealth, + useIntegrationSessionLogs, + useIntegrationSessions, + useJobs, + useProviders, + useRunTelemetry, + useRuns, + useSetIbmApiKey, + useStopIntegrationSession, +} from "../../api/hooks"; +import type { + GkpOscillatorStateSample, + IntegrationAdapterId, + IntegrationSession, + IntegrationSessionConfig, + IntegrationSessionStatus, + Provider, + Run, + RunTelemetry, + SyndromeSample, +} from "../../api/types"; +import { useDataMode } from "../../data/dataMode"; +import { useSessionControl, type SessionLaunchMode } from "../../data/sessionControl"; +import { + DECODERS, + DECODER_PROFILES, + decoderMatchesKey, + decoderLabel, + parseDecoderKey, +} from "../../data/decoders"; +import type { DecoderKey } from "../../data/decoders"; +import { gkpHealth, gkpJobs, gkpProviders, gkpRunTelemetry, gkpRuns } from "../../data/gkpFixtures"; +import { + SCIENTIFIC_CARD_CONTRACTS, + SCIENTIFIC_FIELD_LABELS, + SCIENTIFIC_PRIMARY_CARD_ORDER, + SCIENTIFIC_SECONDARY_CARD_ORDER, + type ScientificCardKey, + type ScientificField, +} from "../scientific/contracts"; +import { ScientificEmptyState } from "../scientific/ScientificEmptyState"; +import { ScientificIntegrityAlert } from "../scientific/ScientificIntegrityAlert"; +import { ScientificMetricCard } from "../scientific/ScientificMetricCard"; +import { SessionLauncherButton } from "../scientific/SessionLauncherButton"; +import { ScientificStateBanner } from "../scientific/ScientificStateBanner"; +import { StartBenchmarkSessionDialog } from "../scientific/StartBenchmarkSessionDialog"; +import { + StartCircuitDesignDialog, + type CircuitDesignDraft, + type CircuitProviderFamily, +} from "../scientific/StartCircuitDesignDialog"; +import { StartIbmApiKeyDialog } from "../scientific/StartIbmApiKeyDialog"; +import { StartReplaySessionDialog } from "../scientific/StartReplaySessionDialog"; +import { + decoderRowMatchesActive, + resolveScientificState, + scientificStateLabel, + scientificStateStatusClass, +} from "../scientific/stateMachine"; + +type DashboardChart = "noise" | "success" | "error" | "latency"; +type TimeRangeFilter = "1h" | "6h" | "24h" | "7d"; +type DashboardRole = "viewer" | "operator" | "admin"; +type EncodingMapMode = "surface" | "gkp"; +type OuterCodeDistance = 3 | 5 | 7; + +interface MonitoringPoint { + slot: string; + noise: number; + success: number; + error: number; + latency: number; +} + +interface MonitoringPointWithCompare extends MonitoringPoint { + compareNoise?: number | null; + compareSuccess?: number | null; + compareError?: number | null; + compareLatency?: number | null; +} + +interface MonitoringSeriesResult { + series: MonitoringPoint[]; + hasDecoderSignal: boolean; +} + +interface PhysicalNoisePoint { + round: number; + physicalErrorPct: number; + photonLossPct: number; + displacementSigma: number; +} + +interface OperationalKpiCardModel { + key: string; + label: string; + value: string; + trendText: string; + trendDelta: number; + trendUpGood: boolean; +} + +interface WorkflowAlertItem { + id: string; + level: "critical" | "warning" | "info"; + title: string; + detail: string; + metric: string; +} + +interface AlertWorkflowState { + acknowledged: boolean; + owner: string; + notes: string; +} + +interface DrilldownState { + source: "physical" | "realtime"; + title: string; + summary: string; + keyValues: Array<{ label: string; value: string }>; + timeline: string[]; +} + +interface ChartEventPayload { + activePayload?: Array<{ payload?: T }>; +} + +interface PhysicalLegendSignal { + id: string; + label: string; + color?: string; + format: (point: PhysicalNoisePoint | null) => string; +} + +interface QecLatticeNode { + key: string; + label: string; + x: number; + y: number; + triggered: boolean; + value: number; +} + +interface QecLatticeEdge { + key: string; + x1: number; + y1: number; + x2: number; + y2: number; +} + +interface QecLatticeModel { + nodes: QecLatticeNode[]; + edges: QecLatticeEdge[]; + columns: number; + rows: number; +} + +interface GkpOscillatorMapPoint { + key: string; + mode: string; + round: number; + q: number; + p: number; + variance: number; + energy: number; + flagged: boolean; + x: number; + y: number; +} + +function clamp(value: number, min: number, max: number): number { + return Math.min(max, Math.max(min, value)); +} + +function numericValue(value: unknown): number { + if (typeof value === "number") { + return value; + } + if (typeof value === "string") { + const parsed = Number(value); + return Number.isFinite(parsed) ? parsed : 0; + } + if (Array.isArray(value) && value.length > 0) { + return numericValue(value[0]); + } + return 0; +} + +function chartLabel(chart: DashboardChart): string { + if (chart === "noise") { + return "Noise Level"; + } + if (chart === "success") { + return "Success Rate"; + } + if (chart === "error") { + return "Error Rate"; + } + return "Latency Trend"; +} + +const PHYSICAL_LEGEND_SIGNALS: PhysicalLegendSignal[] = [ + { + id: "sigma", + label: "Displacement Sigma", + color: "#3f89ea", + format: (point) => (point ? point.displacementSigma.toFixed(4) : "N/A"), + }, + { + id: "photon-loss", + label: "Photon Loss Rate", + color: "#f0982f", + format: (point) => (point ? `${point.photonLossPct.toFixed(3)}%` : "N/A"), + }, + { + id: "physical-error", + label: "Physical Error Rate", + color: "#e25564", + format: (point) => (point ? `${point.physicalErrorPct.toFixed(3)}%` : "N/A"), + }, +]; + +function providerKindLabel(kind: string): string { + if (kind === "superconducting") { + return "Superconducting Qubits"; + } + if (kind === "photonic") { + return "Photonic"; + } + if (kind === "trapped_ion") { + return "Trapped Ion"; + } + if (kind === "simulated") { + return "Simulated"; + } + return "Other"; +} + +interface QuickLaunchPlan { + adapterId: IntegrationAdapterId; + config: IntegrationSessionConfig; +} + +interface LaunchSessionInput { + mode: SessionLaunchMode; + provider: Provider; + decoders: DecoderKey[]; + datasetHint: string; + circuitDesign?: CircuitDesignDraft; + runSource?: Run | null; + ibmLiveSourceMode?: "metadata" | "qpu"; + ibmInstance?: string; +} + +interface ReplaySourceOption { + runId: string; + datasetLabel: string; + providerName: string; + updatedAtLabel: string; +} + +type ProviderFamily = "xanadu" | "ankaa" | "ibm" | "pennylane" | "qiskit" | "cirq" | "unknown"; + +function sessionModeFromAdapter(adapterId: IntegrationAdapterId): SessionLaunchMode { + if (adapterId === "ibm_superconducting_live") { + return "scientific"; + } + return "replay"; +} + +function resolveProviderFamily(provider: Provider): ProviderFamily { + const identitySignal = [provider.name, provider.contact_email ?? ""] + .join(" ") + .trim() + .toLowerCase(); + // Resolve software stacks from provider identity only; shared notes may mention multiple stacks. + if (identitySignal.includes("qiskit")) { + return "qiskit"; + } + if (identitySignal.includes("cirq")) { + return "cirq"; + } + if (identitySignal.includes("pennylane")) { + return "pennylane"; + } + if (identitySignal.includes("xanadu")) { + return "xanadu"; + } + if (identitySignal.includes("ankaa")) { + return "ankaa"; + } + if (identitySignal.includes("ibm")) { + return "ibm"; + } + + const metadataSignal = [provider.readiness_note ?? "", provider.notes ?? ""] + .join(" ") + .trim() + .toLowerCase(); + if (metadataSignal.includes("xanadu")) { + return "xanadu"; + } + if (metadataSignal.includes("ankaa")) { + return "ankaa"; + } + if (metadataSignal.includes("ibm")) { + return "ibm"; + } + if (provider.kind === "photonic") { + return "xanadu"; + } + if (provider.kind === "superconducting") { + if (provider.supports_live) { + return "ibm"; + } + if (provider.supports_replay) { + return "ankaa"; + } + return "unknown"; + } + return "unknown"; +} + +function familyRequiresNeuralModel(family: ProviderFamily): boolean { + return ( + family === "xanadu" || + family === "pennylane" || + family === "qiskit" || + family === "cirq" + ); +} + +function workflowForProviderFamily(family: ProviderFamily): string | undefined { + if (family === "pennylane" || family === "qiskit" || family === "cirq") { + return "paper_04"; + } + return undefined; +} + +function adapterRequiresNeuralModel(adapterId: IntegrationAdapterId): boolean { + return ( + adapterId === "xanadu_gkp_remote_replay" || + adapterId === "pennylane_surface_replay" || + adapterId === "qiskit_surface_replay" || + adapterId === "cirq_surface_replay" + ); +} + +function supportsSoftwareCircuitDesign(provider: Provider): boolean { + const family = resolveProviderFamily(provider); + return family === "pennylane" || family === "qiskit" || family === "cirq"; +} + +function providerReady(provider: Provider | null): boolean { + return provider != null && provider.status !== "offline"; +} + +function runHasScientificEvidence(run: Run): boolean { + if (run.status === "created") { + return false; + } + const metrics = run.metrics; + if (!metrics) { + return false; + } + return ( + metrics.scientific_validation_ready === true || + metrics.logical_error_rate != null || + metrics.logical_failures != null || + metrics.logical_trials != null || + metrics.physical_error_rate != null || + metrics.physical_error_events != null || + metrics.physical_error_opportunities != null || + metrics.request_line_count != null || + metrics.response_line_count != null || + metrics.rounds != null || + metrics.stabilizer_count != null || + metrics.syndrome_opportunities != null || + metrics.residual_syndrome_events != null || + metrics.expanded_shot_count != null || + metrics.syndrome_satisfaction_rate != null || + (metrics.decoder_exact_metrics?.length ?? 0) > 0 || + (metrics.decoder_rankings?.length ?? 0) > 0 + ); +} + +function scientificTransport(provider: Provider): "live" | "replay" | null { + if (!provider.supports_scientific) { + return null; + } + const family = resolveProviderFamily(provider); + if (family === "ibm") { + if (provider.supports_live) { + return "live"; + } + if (provider.supports_replay) { + return "replay"; + } + return null; + } + if (provider.supports_replay) { + return "replay"; + } + if (provider.supports_live) { + return "live"; + } + return null; +} + +function buildQuickLaunchPlan( + provider: Provider, + _selectedDecoder: DecoderKey, + neuralModelPath: string, + mode: SessionLaunchMode, +): QuickLaunchPlan | null { + const family = resolveProviderFamily(provider); + const scientificMode = scientificTransport(provider); + if (mode === "replay" && !provider.supports_replay) { + return null; + } + if ((mode === "scientific" || mode === "benchmark") && scientificMode == null) { + return null; + } + + if (family === "xanadu") { + if (!provider.supports_replay) { + return null; + } + const trimmedModelPath = neuralModelPath.trim(); + return { + adapterId: "xanadu_gkp_remote_replay", + config: { + remote: "macstudio", + remote_input_root: "examples/results/hardware_integration/downloads/gkp/full", + neural_model_path: trimmedModelPath || undefined, + }, + }; + } + + if (family === "ibm") { + if (mode === "replay") { + return null; + } + if (!provider.supports_live) { + return null; + } + return { + adapterId: "ibm_superconducting_live", + config: { + backend_name: "ibm_kingston", + poll_interval: 30, + }, + }; + } + + if (family === "ankaa") { + if (mode !== "scientific" && mode !== "benchmark" && !provider.supports_replay) { + return null; + } + if ((mode === "scientific" || mode === "benchmark") && scientificMode !== "replay") { + return null; + } + return { + adapterId: "ankaa_superconducting_replay", + config: { + input_path: "hardware_integration/ankaa/superconducting/ankaa_fixture_example.json", + }, + }; + } + + if (family === "pennylane" || family === "qiskit" || family === "cirq") { + if (mode !== "scientific" && mode !== "benchmark" && !provider.supports_replay) { + return null; + } + if ((mode === "scientific" || mode === "benchmark") && scientificMode !== "replay") { + return null; + } + const trimmedModelPath = neuralModelPath.trim(); + const adapterId: IntegrationAdapterId = + family === "pennylane" + ? "pennylane_surface_replay" + : family === "qiskit" + ? "qiskit_surface_replay" + : "cirq_surface_replay"; + return { + adapterId, + config: { + simulator_code_family: "surface", + simulator_shots: 240, + simulator_distance: 5, + simulator_rounds: 4, + simulator_error_rate: 0.08, + simulator_sigma: 0.18, + neural_model_path: trimmedModelPath || undefined, + }, + }; + } + + return null; +} + +function parseTimeRange(value: string | null): TimeRangeFilter { + if (value === "6h" || value === "24h" || value === "7d") { + return value; + } + return "1h"; +} + +function parseRole(value: string | null): DashboardRole { + if (value === "viewer" || value === "operator") { + return value; + } + return "admin"; +} + +function parseOuterCodeDistance(value: string | null): OuterCodeDistance { + if (value === "5") { + return 5; + } + if (value === "7") { + return 7; + } + return 3; +} + +function parseBooleanFlag(value: string | null): boolean { + return value === "1" || value === "true" || value === "yes"; +} + +function percentDelta(current: number, previous: number): number { + if (!Number.isFinite(current) || !Number.isFinite(previous)) { + return 0; + } + const baseline = Math.max(1e-9, Math.abs(previous)); + return ((current - previous) / baseline) * 100; +} + +function average(values: number[]): number { + if (values.length === 0) { + return 0; + } + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +function percentile(values: number[], p: number): number { + if (values.length === 0) { + return 0; + } + const sorted = [...values].sort((a, b) => a - b); + const index = Math.min(sorted.length - 1, Math.max(0, Math.floor((p / 100) * sorted.length))); + return sorted[index]; +} + +function formatTrend(deltaPct: number): string { + const absValue = Math.abs(deltaPct); + const direction = deltaPct >= 0 ? "▲" : "▼"; + return `${direction} ${absValue.toFixed(1)}%`; +} + +function asCount(value: number | null | undefined): number | null { + if (value == null || !Number.isFinite(value) || value < 0) { + return null; + } + return Math.trunc(value); +} + +function formatCount(value: number | null | undefined): string { + const count = asCount(value); + return count == null ? "—" : count.toLocaleString(); +} + +function formatCompactCount(value: number | null | undefined): string { + const count = asCount(value); + if (count == null) { + return "—"; + } + const units: Array<{ threshold: number; suffix: string }> = [ + { threshold: 1_000_000_000_000, suffix: "T" }, + { threshold: 1_000_000_000, suffix: "B" }, + { threshold: 1_000_000, suffix: "M" }, + { threshold: 1_000, suffix: "K" }, + ]; + for (const unit of units) { + if (count >= unit.threshold) { + const scaled = count / unit.threshold; + const digits = scaled >= 100 ? 0 : scaled >= 10 ? 1 : 2; + return `${scaled.toFixed(digits)}${unit.suffix}`; + } + } + return count.toLocaleString(); +} + +function formatPercentWithCounts( + numerator: number | null | undefined, + denominator: number | null | undefined, + digits = 2, +): string { + const n = asCount(numerator); + const d = asCount(denominator); + if (n == null || d == null || d <= 0 || n > d) { + return "—"; + } + const pct = (n / d) * 100; + return `${n.toLocaleString()} / ${d.toLocaleString()} = ${pct.toFixed(digits)}%`; +} + +function formatRatioWithCounts( + numerator: number | null | undefined, + denominator: number | null | undefined, + digits = 3, +): string { + const n = asCount(numerator); + const d = asCount(denominator); + if (n == null || d == null || d <= 0) { + return "—"; + } + return `${n.toLocaleString()} / ${d.toLocaleString()} = ${(n / d).toFixed(digits)}`; +} + +function formatOverheadMapping( + rounds: number | null | undefined, + stabilizerCount: number | null | undefined, + providerKind: Provider["kind"] | null | undefined, +): string { + const normalizedRounds = asCount(rounds); + const normalizedStabilizerCount = asCount(stabilizerCount); + if ( + normalizedRounds == null || + normalizedStabilizerCount == null || + normalizedRounds <= 0 || + normalizedStabilizerCount <= 0 + ) { + return "—"; + } + const mappedOverhead = Math.round( + normalizedStabilizerCount * normalizedRounds * (providerKind === "photonic" ? 1.4 : 2.1), + ); + if (providerKind === "photonic") { + return `${mappedOverhead.toLocaleString()} CV states / logical mode`; + } + return `${mappedOverhead.toLocaleString()} physical qubits / logical qubit`; +} + +function formatAgo(isoText: string | null | undefined): string { + if (!isoText) { + return "unknown"; + } + const parsed = new Date(isoText).getTime(); + if (!Number.isFinite(parsed)) { + return "unknown"; + } + const deltaMs = Date.now() - parsed; + const deltaMins = Math.max(0, Math.floor(deltaMs / 60_000)); + if (deltaMins < 1) { + return "just now"; + } + if (deltaMins < 60) { + return `${deltaMins}m ago`; + } + const hours = Math.floor(deltaMins / 60); + if (hours < 24) { + return `${hours}h ago`; + } + const days = Math.floor(hours / 24); + return `${days}d ago`; +} + +function formatClock(isoText: string | null | undefined): string { + if (!isoText) { + return "--:--:--"; + } + const date = new Date(isoText); + if (!Number.isFinite(date.getTime())) { + return "--:--:--"; + } + return date.toLocaleTimeString([], { hour12: false }); +} + +function sessionStatusLabel(status: IntegrationSessionStatus): string { + if (status === "starting") { + return "Starting"; + } + if (status === "running") { + return "Running"; + } + if (status === "finished") { + return "Finished"; + } + if (status === "cancelled") { + return "Cancelled"; + } + return "Failed"; +} + +function sessionStatusLevel(status: IntegrationSessionStatus): "info" | "warn" | "critical" | "ok" { + if (status === "finished") { + return "ok"; + } + if (status === "failed") { + return "critical"; + } + if (status === "cancelled") { + return "warn"; + } + return "info"; +} + +function inferLogLevel(stream: "stdout" | "stderr" | "system", line: string): "info" | "warn" | "critical" | "ok" { + const normalized = line.trim().toLowerCase(); + if ( + stream === "stderr" || + normalized.includes("error") || + normalized.includes("failed") || + normalized.includes("exception") || + normalized.includes("traceback") || + normalized.includes("timed out") || + normalized.includes("timeout") + ) { + return "critical"; + } + if (normalized.includes("warn")) { + return "warn"; + } + if ( + normalized.includes("started") || + normalized.includes("running") || + normalized.includes("completed") || + normalized.includes("finished") || + normalized.includes("success") + ) { + return "ok"; + } + return stream === "system" ? "info" : "ok"; +} + +function formatBytes(bytes: number): string { + if (!Number.isFinite(bytes) || bytes <= 0) { + return "0 B"; + } + if (bytes < 1024) { + return `${Math.round(bytes)} B`; + } + if (bytes < 1024 * 1024) { + return `${(bytes / 1024).toFixed(1)} KB`; + } + if (bytes < 1024 * 1024 * 1024) { + return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; + } + return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; +} + +function stabilizerSortKey(label: string): number { + const match = label.match(/\d+/); + if (!match) { + return Number.MAX_SAFE_INTEGER; + } + const parsed = Number(match[0]); + return Number.isFinite(parsed) ? parsed : Number.MAX_SAFE_INTEGER; +} + +function stabilizerLabel(index: number): string { + return `S${(index + 1).toString().padStart(2, "0")}`; +} + +function timeRangeConfig(range: TimeRangeFilter): { points: number; stepMinutes: number } { + if (range === "6h") { + return { points: 72, stepMinutes: 5 }; + } + if (range === "24h") { + return { points: 288, stepMinutes: 5 }; + } + if (range === "7d") { + return { points: 336, stepMinutes: 30 }; + } + return { points: 12, stepMinutes: 5 }; +} + +function formatTimeSlot(totalPoints: number, index: number, stepMinutes: number): string { + const minutesBack = (totalPoints - index - 1) * stepMinutes; + if (minutesBack >= 1_440) { + const days = Math.round((minutesBack / 1_440) * 10) / 10; + return `T-${days}d`; + } + if (minutesBack >= 60) { + const hours = Math.round((minutesBack / 60) * 10) / 10; + return `T-${hours}h`; + } + return `T-${minutesBack}m`; +} + +function downsampleSeries(series: T[], maxPoints: number): T[] { + if (series.length <= maxPoints) { + return series; + } + const stride = Math.ceil(series.length / maxPoints); + return series.filter((_, index) => index % stride === 0 || index === series.length - 1); +} + +function extractChartPayload(event: unknown): T | null { + if (!event || typeof event !== "object") { + return null; + } + const payloadContainer = event as ChartEventPayload; + const firstPayload = payloadContainer.activePayload?.[0]; + if (!firstPayload || !firstPayload.payload) { + return null; + } + return firstPayload.payload; +} + +function metricValue(point: MonitoringPointWithCompare, chart: DashboardChart, compare: boolean): number { + if (chart === "noise") { + return compare ? point.compareNoise ?? 0 : point.noise; + } + if (chart === "success") { + return compare ? point.compareSuccess ?? 0 : point.success; + } + if (chart === "error") { + return compare ? point.compareError ?? 0 : point.error; + } + return compare ? point.compareLatency ?? 0 : point.latency; +} + +function buildMonitoringSeries( + decoder: DecoderKey, + points: number, + stepMinutes: number, + isMock: boolean, + jobsCount: number, + runsCount: number, + providerCount: number, +): MonitoringPoint[] { + const noiseBase = isMock ? 0.017 : 0.013; + const successBase = isMock ? 97.8 : 96.6; + const latencyBase = isMock ? 46 : 41; + const decoderProfile = DECODER_PROFILES[decoder]; + const providerFactor = clamp(providerCount / 12, 0.1, 1.25); + + return Array.from({ length: points }, (_, index) => { + const wave = Math.sin(index * 0.66) * decoderProfile.waveScale; + const pulse = Math.cos(index * 0.37) * decoderProfile.waveScale; + const loadFactor = jobsCount * 0.06 + runsCount * 0.1; + const success = clamp( + successBase + decoderProfile.successBias + pulse * 1.1 - loadFactor * 0.08 + providerFactor * 0.7, + 87, + 99.8, + ); + const error = clamp(100 - success, 0.2, 12); + const latency = clamp( + latencyBase + decoderProfile.latencyBias + wave * 5.5 + loadFactor * 0.95 + providerFactor * 2.1, + 18, + 180, + ); + const noise = clamp( + noiseBase + decoderProfile.noiseBias + wave * 0.0019 + jobsCount * 0.00025 + providerFactor * 0.00035, + 0.004, + 0.04, + ); + + return { + slot: formatTimeSlot(points, index, stepMinutes), + noise: Number(noise.toFixed(4)), + success: Number(success.toFixed(2)), + error: Number(error.toFixed(2)), + latency: Number(latency.toFixed(1)), + }; + }); +} + +function buildMonitoringSeriesFromTelemetry( + decoder: DecoderKey, + telemetry: RunTelemetry | null | undefined, + points: number, + stepMinutes: number, +): MonitoringSeriesResult { + const emptySeries = Array.from({ length: points }, (_, index) => ({ + slot: formatTimeSlot(points, index, stepMinutes), + noise: 0, + success: 0, + error: 0, + latency: 0, + })); + if (!telemetry) { + return { series: emptySeries, hasDecoderSignal: false }; + } + + const inferredRounds = Math.max( + telemetry.rounds ?? 0, + telemetry.syndrome_samples.reduce((maxRound, sample) => Math.max(maxRound, sample.round + 1), 0), + telemetry.decoder_interventions.reduce((maxRound, row) => Math.max(maxRound, row.round + 1), 0), + ); + const rounds = Math.max(1, inferredRounds); + + const syndromeByRound = new Map(); + telemetry.syndrome_samples.forEach((sample) => { + const current = syndromeByRound.get(sample.round) ?? { triggered: 0, total: 0 }; + current.total += 1; + if (sample.is_triggered) { + current.triggered += 1; + } + syndromeByRound.set(sample.round, current); + }); + + const decoderRows = telemetry.decoder_interventions.filter((row) => + decoderMatchesKey(row.decoder, decoder), + ); + const hasDecoderSignal = decoderRows.length > 0; + const interventionsByRound = new Map(); + decoderRows.forEach((row) => { + const current = interventionsByRound.get(row.round) ?? { flips: 0, residual: 0 }; + current.flips += row.flips; + current.residual += row.residual_weight; + interventionsByRound.set(row.round, current); + }); + + const sortedNoise = [...telemetry.noise_samples].sort((left, right) => left.index - right.index); + const noiseForRound = (round: number) => { + if (sortedNoise.length === 0) { + return telemetry.warning_rate ?? 0.012; + } + const safeIndex = Math.min(sortedNoise.length - 1, Math.max(0, round)); + return sortedNoise[safeIndex]?.physical_error_rate ?? telemetry.warning_rate ?? 0.012; + }; + + const series = Array.from({ length: points }, (_, index) => { + const startRound = Math.floor((index * rounds) / points); + const endRound = Math.max(startRound + 1, Math.floor(((index + 1) * rounds) / points)); + const boundedEnd = Math.min(rounds, endRound); + + let bucketRounds = 0; + let totalTriggered = 0; + let totalChecks = 0; + let flips = 0; + let residual = 0; + let noiseAccum = 0; + + for (let round = startRound; round < boundedEnd; round += 1) { + bucketRounds += 1; + const syndromeStats = syndromeByRound.get(round); + if (syndromeStats) { + totalTriggered += syndromeStats.triggered; + totalChecks += syndromeStats.total; + } + const intervention = interventionsByRound.get(round); + if (intervention) { + flips += intervention.flips; + residual += intervention.residual; + } + noiseAccum += noiseForRound(round); + } + + const roundsInBucket = Math.max(1, bucketRounds); + const triggerRatio = totalChecks > 0 ? totalTriggered / totalChecks : 0; + const flipsMean = flips / roundsInBucket; + const residualMean = residual / roundsInBucket; + const noiseBase = noiseAccum / roundsInBucket; + const noise = clamp(noiseBase + triggerRatio * 0.01, 0.001, 0.08); + + const success = hasDecoderSignal + ? clamp(99.4 - flipsMean * 2.6 - residualMean * 3.4 - triggerRatio * 19 - noise * 330, 82, 99.9) + : clamp(88.0 - triggerRatio * 18 - noise * 240, 70, 95); + const error = clamp(100 - success, 0.05, 30); + const latency = hasDecoderSignal + ? clamp(18 + flipsMean * 4.8 + residualMean * 6.4 + triggerRatio * 20 + noise * 620, 8, 320) + : clamp(9 + triggerRatio * 11 + noise * 380, 4, 180); + + return { + slot: formatTimeSlot(points, index, stepMinutes), + noise: Number(noise.toFixed(4)), + success: Number(success.toFixed(2)), + error: Number(error.toFixed(2)), + latency: Number(latency.toFixed(1)), + }; + }); + + return { series, hasDecoderSignal }; +} + +export function DecoderDashboard() { + const navigate = useNavigate(); + const [activeChart, setActiveChart] = useState("noise"); + const [encodingMapMode, setEncodingMapMode] = useState("surface"); + const [isPhysicalPanelCollapsed, setPhysicalPanelCollapsed] = useState(false); + const [drilldown, setDrilldown] = useState(null); + const [alertWorkflow, setAlertWorkflow] = useState>({}); + const [showLiveConsole, setShowLiveConsole] = useState(true); + const [showOperationalWorkflow, setShowOperationalWorkflow] = useState(true); + const [opsLogCursor, setOpsLogCursor] = useState(0); + const [quickLaunchMessage, setQuickLaunchMessage] = useState(null); + const [quickLaunchTone, setQuickLaunchTone] = useState<"info" | "success" | "error">("info"); + const [sessionLauncherMenuOpen, setSessionLauncherMenuOpen] = useState(false); + const [circuitDesignDialogOpen, setCircuitDesignDialogOpen] = useState(false); + const [pendingCircuitLaunch, setPendingCircuitLaunch] = useState(null); + const [benchmarkDialogOpen, setBenchmarkDialogOpen] = useState(false); + const [replayDialogOpen, setReplayDialogOpen] = useState(false); + const [ibmApiKeyDialogOpen, setIbmApiKeyDialogOpen] = useState(false); + const [ibmApiKeyInput, setIbmApiKeyInput] = useState(""); + const [ibmLiveSourceMode, setIbmLiveSourceMode] = useState<"metadata" | "qpu">("metadata"); + const [ibmInstanceInput, setIbmInstanceInput] = useState(""); + const [ibmApiKeyError, setIbmApiKeyError] = useState(null); + const [pendingIbmLaunch, setPendingIbmLaunch] = useState(null); + const [benchmarkDecoders, setBenchmarkDecoders] = useState(() => DECODERS.map((decoder) => decoder.key)); + const [replaySourceRunId, setReplaySourceRunId] = useState(""); + const [activeHomeSessionSnapshot, setActiveHomeSessionSnapshot] = useState(null); + const [healthProbeLatencyMs, setHealthProbeLatencyMs] = useState(null); + const healthProbeStartRef = useRef(null); + const homeSessionStatusRef = useRef(null); + const [searchParams, setSearchParams] = useSearchParams(); + const { + state: sessionControlState, + beginLaunch, + markRunning, + markStopping, + markStopped, + markFailed, + setActiveContext, + clearError, + } = useSessionControl(); + const sessionControlStatusRef = useRef(sessionControlState.status); + const { + mode, + isApi, + isMock, + systemOff, + setSystemOff, + systemArmed, + armSystem, + activeDecoder, + setActiveDecoder, + neuralModelPath, + setNeuralModelPath, + } = useDataMode(); + const apiConnected = isApi && !systemOff; + const apiEnabled = apiConnected && systemArmed; + const activeHomeRunId = sessionControlState.activeRunId; + const activeHomeSessionId = sessionControlState.activeSessionId; + + const timeRangeFilter = parseTimeRange(searchParams.get("range")); + const providerFilter = searchParams.get("provider") ?? "all"; + const compareMode = parseBooleanFlag(searchParams.get("compare")); + const role = parseRole(searchParams.get("role")); + const outerCodeDistance = parseOuterCodeDistance(searchParams.get("outerDistance")); + const compareDecoderParam = parseDecoderKey(searchParams.get("compareDecoder")); + const fallbackCompareDecoder = + DECODERS.find((decoder) => decoder.key !== activeDecoder)?.key ?? activeDecoder; + const compareDecoder = + compareDecoderParam && compareDecoderParam !== activeDecoder + ? compareDecoderParam + : fallbackCompareDecoder; + const canEditWorkflow = role !== "viewer"; + + const setFilterParam = (key: string, value: string, defaultValue: string) => { + setSearchParams((currentParams) => { + const nextParams = new URLSearchParams(currentParams); + if (!value || value === defaultValue) { + nextParams.delete(key); + } else { + nextParams.set(key, value); + } + return nextParams; + }); + }; + + const healthQuery = useHealth({ enabled: apiEnabled }); + const providersQuery = useProviders({ enabled: apiConnected, refetchInterval: 15_000 }); + const jobsQuery = useJobs({ enabled: apiEnabled, refetchInterval: 2_500 }); + const runsQuery = useRuns({ enabled: apiEnabled, refetchInterval: 2_500 }); + const sessionsQuery = useIntegrationSessions({ enabled: apiEnabled, refetchInterval: 2_000 }); + const createRunMutation = useCreateRun(); + const createSessionMutation = useCreateIntegrationSession(); + const setIbmApiKeyMutation = useSetIbmApiKey(); + const stopSessionMutation = useStopIntegrationSession(); + + const healthDataRaw = isMock ? gkpHealth : healthQuery.data; + const healthData = systemOff ? null : healthDataRaw; + const providerCatalogData = isMock ? gkpProviders : providersQuery.data ?? []; + const providersData = systemArmed ? providerCatalogData : []; + const jobsData = systemArmed ? (isMock ? gkpJobs : jobsQuery.data ?? []) : []; + const runsData = systemArmed ? (isMock ? gkpRuns : runsQuery.data ?? []) : []; + const groupedProviderOptions = useMemo(() => { + const hardware: Provider[] = []; + const simulators: Provider[] = []; + providerCatalogData.forEach((provider) => { + if (provider.kind === "simulated") { + simulators.push(provider); + return; + } + hardware.push(provider); + }); + return { hardware, simulators }; + }, [providerCatalogData]); + const integrationSessions = systemArmed ? (isMock ? [] : sessionsQuery.data ?? []) : []; + const sortedIntegrationSessions = useMemo(() => { + return [...integrationSessions].sort( + (left, right) => new Date(right.updated_at).getTime() - new Date(left.updated_at).getTime(), + ); + }, [integrationSessions]); + + const selectableProviders = useMemo(() => { + return providerCatalogData.filter((provider) => { + if (providerFilter !== "all" && provider.id !== providerFilter) { + return false; + } + return true; + }); + }, [providerCatalogData, providerFilter]); + + const filteredProviders = useMemo( + () => (systemArmed ? selectableProviders : []), + [selectableProviders, systemArmed], + ); + + const scopedProviders = useMemo(() => (systemOff ? [] : filteredProviders), [filteredProviders, systemOff]); + + const [launcherProviderId, setLauncherProviderId] = useState("auto"); + const quickLaunchProvider = useMemo(() => { + if (providerFilter !== "all") { + return selectableProviders[0] ?? null; + } + const preferred = selectableProviders.find((provider) => !provider.name.toLowerCase().includes("ibm")); + return preferred ?? selectableProviders[0] ?? null; + }, [providerFilter, selectableProviders]); + const launcherProviderOptions = useMemo(() => { + if (selectableProviders.length === 0) { + return []; + } + return selectableProviders.map((provider) => ({ + id: provider.id, + name: provider.name, + status: provider.status, + kind: provider.kind, + })); + }, [selectableProviders]); + useEffect(() => { + if (launcherProviderOptions.length === 0) { + if (launcherProviderId !== "auto") { + setLauncherProviderId("auto"); + } + return; + } + if ( + launcherProviderId === "auto" || + !launcherProviderOptions.some((provider) => provider.id === launcherProviderId) + ) { + setLauncherProviderId(launcherProviderOptions[0].id); + } + }, [launcherProviderId, launcherProviderOptions]); + const launchProvider = useMemo(() => { + if (launcherProviderId === "auto") { + return quickLaunchProvider; + } + return selectableProviders.find((provider) => provider.id === launcherProviderId) ?? quickLaunchProvider; + }, [launcherProviderId, quickLaunchProvider, selectableProviders]); + + const scopedProviderIds = useMemo( + () => new Set(scopedProviders.map((provider) => provider.id)), + [scopedProviders], + ); + + const scopedJobs = useMemo(() => { + return jobsData.filter((job) => scopedProviderIds.has(job.provider_id)); + }, [jobsData, scopedProviderIds]); + + const scopedRuns = useMemo(() => { + const filtered = runsData.filter((run) => { + if (!scopedProviderIds.has(run.provider_id)) { + return false; + } + return true; + }); + return [...filtered].sort((a, b) => new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime()); + }, [runsData, scopedProviderIds]); + + const providerById = useMemo( + () => new Map(providerCatalogData.map((provider) => [provider.id, provider])), + [providerCatalogData], + ); + + const runById = useMemo(() => new Map(runsData.map((run) => [run.id, run])), [runsData]); + const scopedIntegrationSessions = useMemo(() => { + return sortedIntegrationSessions.filter((session) => { + const run = runById.get(session.run_id); + if (!run) { + return providerFilter === "all"; + } + return scopedProviderIds.has(run.provider_id); + }); + }, [providerFilter, runById, scopedProviderIds, sortedIntegrationSessions]); + const activeIntegrationSession = useMemo(() => { + if (!isApi) { + return null; + } + if (activeHomeSessionId) { + const exactSession = + scopedIntegrationSessions.find((session) => session.id === activeHomeSessionId) ?? + sortedIntegrationSessions.find((session) => session.id === activeHomeSessionId); + if (exactSession) { + return exactSession; + } + if (activeHomeSessionSnapshot?.id === activeHomeSessionId) { + return activeHomeSessionSnapshot; + } + } + if (activeHomeRunId) { + const byRun = + scopedIntegrationSessions.find((session) => session.run_id === activeHomeRunId) ?? + sortedIntegrationSessions.find((session) => session.run_id === activeHomeRunId); + if (byRun) { + return byRun; + } + } + return ( + scopedIntegrationSessions.find( + (session) => session.status === "running" || session.status === "starting", + ) ?? + sortedIntegrationSessions.find( + (session) => session.status === "running" || session.status === "starting", + ) ?? + null + ); + }, [ + activeHomeRunId, + activeHomeSessionId, + activeHomeSessionSnapshot, + isApi, + scopedIntegrationSessions, + sortedIntegrationSessions, + ]); + const activeSessionStreaming = + activeIntegrationSession?.status === "running" || activeIntegrationSession?.status === "starting"; + + useEffect(() => { + if (!activeHomeSessionId) { + homeSessionStatusRef.current = null; + return; + } + const homeSession = + sortedIntegrationSessions.find((session) => session.id === activeHomeSessionId) ?? + (activeHomeSessionSnapshot?.id === activeHomeSessionId ? activeHomeSessionSnapshot : null); + if (!homeSession) { + return; + } + if (homeSessionStatusRef.current === homeSession.status) { + return; + } + homeSessionStatusRef.current = homeSession.status; + + const shortId = homeSession.id.slice(0, 8).toUpperCase(); + if (homeSession.status === "finished") { + markStopped({ preserveRun: true }); + setQuickLaunchTone("success"); + setQuickLaunchMessage(`Session #${shortId} finished.`); + void runsQuery.refetch(); + void sessionsQuery.refetch(); + } else if (homeSession.status === "failed") { + markFailed(homeSession.last_error ?? `Session #${shortId} failed.`); + setQuickLaunchTone("error"); + setQuickLaunchMessage( + homeSession.last_error + ? `Session #${shortId} failed: ${homeSession.last_error}` + : `Session #${shortId} failed.`, + ); + void runsQuery.refetch(); + void sessionsQuery.refetch(); + } else if (homeSession.status === "cancelled") { + markStopped({ preserveRun: true }); + setQuickLaunchTone("info"); + setQuickLaunchMessage(`Session #${shortId} cancelled.`); + void runsQuery.refetch(); + void sessionsQuery.refetch(); + } + }, [ + activeHomeSessionId, + activeHomeSessionSnapshot, + markFailed, + markStopped, + runsQuery, + sessionsQuery, + sortedIntegrationSessions, + ]); + + useEffect(() => { + if (!activeIntegrationSession) { + return; + } + if (activeIntegrationSession.status !== "running" && activeIntegrationSession.status !== "starting") { + return; + } + const resolvedMode = sessionControlState.mode ?? sessionModeFromAdapter(activeIntegrationSession.adapter_id); + if ( + sessionControlState.activeSessionId === activeIntegrationSession.id && + sessionControlState.activeRunId === activeIntegrationSession.run_id && + sessionControlState.status === "running" + ) { + return; + } + setActiveContext({ + runId: activeIntegrationSession.run_id, + sessionId: activeIntegrationSession.id, + mode: resolvedMode, + }); + }, [ + activeIntegrationSession, + sessionControlState.activeRunId, + sessionControlState.activeSessionId, + sessionControlState.mode, + sessionControlState.status, + setActiveContext, + ]); + + const latestScopedRun = scopedRuns.length > 0 ? scopedRuns[scopedRuns.length - 1] : null; + const latestScopedRunWithEvidence = useMemo(() => { + for (let index = scopedRuns.length - 1; index >= 0; index -= 1) { + const candidate = scopedRuns[index]; + if (runHasScientificEvidence(candidate)) { + return candidate; + } + } + return null; + }, [scopedRuns]); + const passiveRun = latestScopedRunWithEvidence ?? latestScopedRun; + const activeRunId = activeHomeRunId ?? activeIntegrationSession?.run_id ?? passiveRun?.id ?? null; + const activeRun = activeRunId ? runById.get(activeRunId) ?? null : passiveRun; + const activeRunStreaming = activeRun?.status === "running" || activeRun?.status === "created"; + const streamWarmupActive = activeSessionStreaming || activeRunStreaming; + const showingHistoricalEvidenceRun = + !activeHomeRunId && + !activeIntegrationSession && + latestScopedRun != null && + latestScopedRunWithEvidence != null && + latestScopedRun.id !== latestScopedRunWithEvidence.id; + const runTelemetryScientificQuery = useRunTelemetry(activeRunId, { + enabled: apiEnabled && Boolean(activeRunId), + scientificMode: true, + refetchInterval: streamWarmupActive ? 1_000 : 3_000, + }); + const runTelemetryWarmupQuery = useRunTelemetry(activeRunId, { + enabled: apiEnabled && Boolean(activeRunId) && streamWarmupActive, + refetchInterval: streamWarmupActive ? 1_000 : 3_000, + }); + const sessionLogsQuery = useIntegrationSessionLogs(activeIntegrationSession?.id ?? null, 220, { + enabled: apiEnabled && Boolean(activeIntegrationSession?.id), + refetchInterval: activeSessionStreaming ? 1_000 : 4_000, + }); + const runTelemetryScientific = + isMock && activeRunId === gkpRunTelemetry.run_id + ? gkpRunTelemetry + : isMock || systemOff + ? null + : runTelemetryScientificQuery.data ?? null; + const runTelemetryWarmup = isMock || systemOff ? null : runTelemetryWarmupQuery.data ?? null; + const usingWarmupTelemetry = + !isMock && streamWarmupActive && runTelemetryScientific == null && runTelemetryWarmup != null; + const runTelemetry = + isMock && activeRunId === gkpRunTelemetry.run_id + ? gkpRunTelemetry + : isMock || systemOff + ? null + : runTelemetryScientific ?? (usingWarmupTelemetry ? runTelemetryWarmup : null); + const runTelemetryNotFound = + !isMock && + runTelemetryScientificQuery.error instanceof ApiError && + runTelemetryScientificQuery.error.status === 404 && + !usingWarmupTelemetry; + const runTelemetryHardError = + !systemOff && + isApi && + Boolean(activeRunId) && + (runTelemetryScientificQuery.isError && + !(runTelemetryScientificQuery.error instanceof ApiError && runTelemetryScientificQuery.error.status === 404)); + const telemetryInitializing = + !systemOff && isApi && Boolean(activeRunId) && streamWarmupActive && !runTelemetry && !runTelemetryHardError; + const telemetryUnavailableForRun = + !systemOff && + isApi && + Boolean(activeRunId) && + runTelemetryNotFound && + !telemetryInitializing; + const syndromeSamples = runTelemetry?.syndrome_samples ?? []; + + useEffect(() => { + const previous = sessionControlStatusRef.current; + const current = sessionControlState.status; + if ( + isApi && + activeRunId && + (previous === "running" || previous === "stopping") && + current === "idle" + ) { + void runsQuery.refetch(); + void sessionsQuery.refetch(); + void runTelemetryScientificQuery.refetch(); + if (streamWarmupActive) { + void runTelemetryWarmupQuery.refetch(); + } + } + sessionControlStatusRef.current = current; + }, [ + activeRunId, + isApi, + runTelemetryScientificQuery, + runTelemetryWarmupQuery, + runsQuery, + sessionControlState.status, + sessionsQuery, + streamWarmupActive, + ]); + + const healthy = healthData?.status.toLowerCase() === "ok"; + const providerCount = scopedProviders.length; + const jobsCount = scopedJobs.length; + const runsCount = scopedRuns.length; + const baseProviderCount = providersData.length; + const baseJobsCount = jobsData.length; + const baseRunsCount = runsData.length; + const uptimeSeconds = healthData ? Math.max(0, Math.floor(healthData.uptime_seconds)) : 0; + + const anyApiLoading = + !systemOff && + isApi && + (healthQuery.isLoading || + providersQuery.isLoading || + jobsQuery.isLoading || + runsQuery.isLoading || + sessionsQuery.isLoading); + const anyApiError = + !systemOff && + isApi && + (healthQuery.isError || + providersQuery.isError || + jobsQuery.isError || + runsQuery.isError || + sessionsQuery.isError); + const noApiEntities = + isApi && + !anyApiLoading && + !anyApiError && + baseProviderCount === 0 && + baseJobsCount === 0 && + baseRunsCount === 0; + + useEffect(() => { + if (!isApi) { + healthProbeStartRef.current = null; + setHealthProbeLatencyMs(45); + return; + } + + if (healthQuery.fetchStatus === "fetching") { + if (healthProbeStartRef.current === null) { + healthProbeStartRef.current = + typeof performance !== "undefined" && typeof performance.now === "function" + ? performance.now() + : Date.now(); + } + return; + } + + if (healthProbeStartRef.current !== null) { + const now = + typeof performance !== "undefined" && typeof performance.now === "function" + ? performance.now() + : Date.now(); + const elapsed = now - healthProbeStartRef.current; + healthProbeStartRef.current = null; + if (Number.isFinite(elapsed) && elapsed >= 0) { + setHealthProbeLatencyMs(Math.round(elapsed)); + } + } + }, [healthQuery.fetchStatus, isApi]); + + useEffect(() => { + if (!sessionLauncherMenuOpen) { + return; + } + const onWindowClick = (event: MouseEvent) => { + const target = event.target; + if (!(target instanceof Element)) { + return; + } + if (target.closest(".session-launcher-split")) { + return; + } + setSessionLauncherMenuOpen(false); + }; + window.addEventListener("click", onWindowClick); + return () => window.removeEventListener("click", onWindowClick); + }, [sessionLauncherMenuOpen]); + + const activeRunsCount = useMemo( + () => scopedRuns.filter((run) => run.status === "created" || run.status === "running").length, + [scopedRuns], + ); + const queuedJobsCount = useMemo( + () => scopedJobs.filter((job) => job.status === "queued").length, + [scopedJobs], + ); + const latestProviderUpdate = useMemo(() => { + if (scopedProviders.length === 0) { + return null; + } + return scopedProviders + .map((provider) => provider.updated_at) + .sort((a, b) => new Date(b).getTime() - new Date(a).getTime())[0]; + }, [scopedProviders]); + const latestJobUpdate = useMemo(() => { + if (scopedJobs.length === 0) { + return null; + } + return scopedJobs + .map((job) => job.updated_at) + .sort((a, b) => new Date(b).getTime() - new Date(a).getTime())[0]; + }, [scopedJobs]); + const scopePayloadBytes = useMemo(() => { + const snapshot = { + providers: scopedProviders, + jobs: scopedJobs, + runs: scopedRuns, + telemetry: runTelemetry, + }; + const serialized = JSON.stringify(snapshot); + if (typeof Blob !== "undefined") { + return new Blob([serialized]).size; + } + return serialized.length; + }, [runTelemetry, scopedJobs, scopedProviders, scopedRuns]); + + const hardwareMix = useMemo(() => { + const mix = scopedProviders.reduce( + (acc, provider) => { + const label = providerKindLabel(provider.kind); + acc[label] = (acc[label] ?? 0) + 1; + return acc; + }, + {} as Record, + ); + return Object.entries(mix) + .map(([label, count]) => ({ label, count })) + .sort((a, b) => b.count - a.count); + }, [scopedProviders]); + + const physicalNoiseData = useMemo(() => { + if (!runTelemetry || !runTelemetry.noise_samples || runTelemetry.noise_samples.length === 0) { + return []; + } + return runTelemetry.noise_samples.map((sample) => ({ + round: sample.index + 1, + physicalErrorPct: Number((sample.physical_error_rate * 100).toFixed(3)), + photonLossPct: Number((sample.photon_loss_rate * 100).toFixed(3)), + displacementSigma: Number(sample.displacement_sigma.toFixed(4)), + })); + }, [runTelemetry]); + const telemetryQueryLoading = + !isMock && + (runTelemetryScientificQuery.isLoading || + (streamWarmupActive && runTelemetryWarmupQuery.isLoading)); + const physicalNoiseLoading = + !systemOff && systemArmed && isApi && Boolean(activeRunId) && telemetryQueryLoading && !runTelemetry; + const physicalNoiseError = runTelemetryHardError; + + const healthBadgeLabel = systemOff + ? "Off" + : !systemArmed + ? "Standby" + : isApi + ? healthQuery.isError + ? "API Error" + : healthQuery.isLoading + ? "Checking" + : healthy + ? "Operational" + : "Degraded" + : healthy + ? "Operational" + : "Checking"; + + const healthBadgeClass = systemOff + ? "badge badge-warning" + : !systemArmed + ? "badge" + : isApi && healthQuery.isError + ? "badge badge-warning" + : `badge ${healthy ? "" : "badge-warning"}`; + + const { points: timelinePoints, stepMinutes } = timeRangeConfig(timeRangeFilter); + const monitorTelemetrySourceAvailable = Boolean( + runTelemetry && + (runTelemetry.noise_samples.length > 0 || + runTelemetry.syndrome_samples.length > 0 || + runTelemetry.decoder_interventions.length > 0), + ); + const monitoringSynthetic = useMemo( + () => + buildMonitoringSeries( + activeDecoder, + timelinePoints, + stepMinutes, + isMock, + jobsCount, + runsCount, + providerCount, + ), + [activeDecoder, isMock, jobsCount, providerCount, runsCount, stepMinutes, timelinePoints], + ); + + const comparisonSynthetic = useMemo( + () => + buildMonitoringSeries( + compareDecoder, + timelinePoints, + stepMinutes, + isMock, + jobsCount, + runsCount, + providerCount, + ), + [compareDecoder, isMock, jobsCount, providerCount, runsCount, stepMinutes, timelinePoints], + ); + + const monitoringTelemetry = useMemo( + () => buildMonitoringSeriesFromTelemetry(activeDecoder, runTelemetry, timelinePoints, stepMinutes), + [activeDecoder, runTelemetry, stepMinutes, timelinePoints], + ); + const comparisonTelemetry = useMemo( + () => buildMonitoringSeriesFromTelemetry(compareDecoder, runTelemetry, timelinePoints, stepMinutes), + [compareDecoder, runTelemetry, stepMinutes, timelinePoints], + ); + + const monitoringRaw = systemOff + ? [] + : monitorTelemetrySourceAvailable + ? monitoringTelemetry.series + : isApi + ? [] + : monitoringSynthetic; + const comparisonRaw = systemOff + ? [] + : monitorTelemetrySourceAvailable + ? comparisonTelemetry.series + : isApi + ? [] + : comparisonSynthetic; + const activeDecoderMissingTelemetry = monitorTelemetrySourceAvailable && !monitoringTelemetry.hasDecoderSignal; + const compareDecoderMissingTelemetry = + monitorTelemetrySourceAvailable && compareMode && !comparisonTelemetry.hasDecoderSignal; + + const monitoringPrimary = useMemo(() => downsampleSeries(monitoringRaw, 120), [monitoringRaw]); + const monitoringCompare = useMemo(() => downsampleSeries(comparisonRaw, 120), [comparisonRaw]); + const monitoringData = useMemo(() => { + return monitoringPrimary.map((point, index) => ({ + ...point, + compareNoise: monitoringCompare[index]?.noise ?? null, + compareSuccess: monitoringCompare[index]?.success ?? null, + compareError: monitoringCompare[index]?.error ?? null, + compareLatency: monitoringCompare[index]?.latency ?? null, + })); + }, [monitoringCompare, monitoringPrimary]); + const monitoringHasRows = monitoringData.length > 0; + + const activityFeed = useMemo(() => { + const items: Array<{ tone: "green" | "red" | "blue"; text: string; time: string }> = []; + + if (isApi && anyApiError) { + items.push({ tone: "red", text: "Backend API reported connectivity failures", time: "now" }); + } else { + items.push({ + tone: "green", + text: `${runsCount} runs tracked in current scope`, + time: activeRun?.updated_at ? formatAgo(activeRun.updated_at) : "live", + }); + } + + items.push({ + tone: providerCount > 0 ? "blue" : "red", + text: `${providerCount} providers available for dispatch in filter`, + time: formatAgo(latestProviderUpdate), + }); + items.push({ + tone: queuedJobsCount > 0 ? "green" : "blue", + text: `${queuedJobsCount} queued jobs in current scope`, + time: formatAgo(latestJobUpdate), + }); + + return items; + }, [ + anyApiError, + isApi, + latestJobUpdate, + latestProviderUpdate, + activeRun?.updated_at, + providerCount, + queuedJobsCount, + runsCount, + ]); + + const perValue = useMemo(() => { + if (physicalNoiseData.length === 0) { + return null; + } + const avg = + physicalNoiseData.reduce((sum, point) => sum + point.physicalErrorPct, 0) / physicalNoiseData.length; + return Number(avg.toFixed(4)); + }, [physicalNoiseData]); + + const latestRunMetrics = activeRun?.metrics ?? null; + const previousRunMetrics = useMemo(() => { + if (scopedRuns.length < 2) { + return null; + } + if (!activeRunId) { + return scopedRuns[scopedRuns.length - 2].metrics ?? null; + } + const activeIndex = scopedRuns.findIndex((run) => run.id === activeRunId); + if (activeIndex === -1) { + return scopedRuns[scopedRuns.length - 2].metrics ?? null; + } + if (activeIndex === 0) { + return null; + } + return scopedRuns[activeIndex - 1].metrics ?? null; + }, [activeRunId, scopedRuns]); + const monitoringSplitIndex = Math.max(1, Math.floor(monitoringRaw.length / 2)); + const previousWindow = monitoringRaw.slice(0, monitoringSplitIndex); + const currentWindow = monitoringRaw.slice(monitoringSplitIndex); + + const p95LatencyCurrent = percentile( + currentWindow.map((point) => point.latency), + 95, + ); + const p95LatencyPrevious = percentile( + previousWindow.map((point) => point.latency), + 95, + ); + const p95LatencyTrend = percentDelta(p95LatencyCurrent, p95LatencyPrevious); + + const throughputCurrent = + average(currentWindow.map((point) => point.success / 100)) * + (1000 / Math.max(1, average(currentWindow.map((point) => point.latency)))); + const throughputPrevious = + average(previousWindow.map((point) => point.success / 100)) * + (1000 / Math.max(1, average(previousWindow.map((point) => point.latency)))); + const throughputTrend = percentDelta(throughputCurrent, throughputPrevious); + + const queueAgedJobs = scopedJobs.filter((job) => job.status === "queued"); + const oldestQueuedAt = queueAgedJobs.reduce((oldest, job) => { + const stamp = new Date(job.created_at).getTime(); + return Number.isFinite(stamp) && stamp < oldest ? stamp : oldest; + }, Number.POSITIVE_INFINITY); + const queueAgeMinutes = + Number.isFinite(oldestQueuedAt) ? Math.max(0, (Date.now() - oldestQueuedAt) / 60_000) : 0; + const queueAgeTrend = percentDelta(queueAgeMinutes, 5); + + const liveSyndromeSatisfactionRate = useMemo(() => { + if (syndromeSamples.length === 0) { + return null; + } + const triggered = syndromeSamples.filter((sample) => sample.is_triggered).length; + const satisfied = 1 - triggered / Math.max(1, syndromeSamples.length); + return clamp(satisfied, 0, 1); + }, [syndromeSamples]); + const syndromeCurrent = + (latestRunMetrics?.syndrome_satisfaction_rate ?? liveSyndromeSatisfactionRate ?? 0) * 100; + const syndromePrevious = (previousRunMetrics?.syndrome_satisfaction_rate ?? syndromeCurrent / 100) * 100; + const syndromeTrend = percentDelta(syndromeCurrent, syndromePrevious); + const dataUpdatedAt = runTelemetry?.updated_at ?? activeRun?.updated_at ?? healthData?.started_at ?? null; + const confidenceScore = systemOff + ? 0 + : clamp(((monitoringData.length + physicalNoiseData.length) / Math.max(1, timelinePoints + 40)) * 100, 6, 99); + const globalRunLogicalErrorRate = latestRunMetrics?.logical_error_rate ?? null; + const previousGlobalRunLogicalErrorRate = previousRunMetrics?.logical_error_rate ?? null; + const globalRunLerTrend = + globalRunLogicalErrorRate != null && previousGlobalRunLogicalErrorRate != null + ? percentDelta(globalRunLogicalErrorRate, previousGlobalRunLogicalErrorRate) + : 0; + + const scientificState = useMemo( + () => + resolveScientificState({ + run: activeRun, + telemetry: runTelemetryScientific, + activeDecoder, + validationPassed: Boolean(activeRun?.metrics?.scientific_validation_ready), + }), + [activeDecoder, activeRun, runTelemetryScientific], + ); + const scientificExactnessLabel = scientificStateLabel(scientificState.state); + const scientificExactnessClass = scientificStateStatusClass(scientificState.state); + const scientificOverheadProviderKind = activeRun + ? providerById.get(activeRun.provider_id)?.kind ?? null + : null; + const scientificZeroBaseline = scientificState.state === "IDLE"; + const scientificCardValues = useMemo>(() => { + if (scientificZeroBaseline) { + return { + ler: "0 / 0 = 0.0000%", + per: "0 / 0 = 0.0000%", + response_ratio: "0 / 0 = 0.000", + rounds: "0", + stabilizer_count: "0", + syndrome_opportunities: "0", + post_correction_overhead: + scientificOverheadProviderKind === "photonic" + ? "0 CV states / logical mode" + : "0 physical qubits / logical qubit", + residual_syndrome_rate: "0 / 0 = 0.0000%", + request_line_count: "0", + response_line_count: "0", + expanded_shot_count: "0", + }; + } + return { + ler: formatPercentWithCounts( + scientificState.signals.logical_failures, + scientificState.signals.logical_trials, + 4, + ), + per: formatPercentWithCounts( + scientificState.signals.physical_error_events, + scientificState.signals.physical_error_opportunities, + 4, + ), + response_ratio: formatRatioWithCounts( + scientificState.signals.response_line_count, + scientificState.signals.request_line_count, + 3, + ), + rounds: formatCount(scientificState.signals.rounds), + stabilizer_count: formatCount(scientificState.signals.stabilizer_count), + syndrome_opportunities: formatCount(scientificState.signals.syndrome_opportunities), + post_correction_overhead: formatOverheadMapping( + scientificState.signals.rounds, + scientificState.signals.stabilizer_count, + scientificOverheadProviderKind, + ), + residual_syndrome_rate: formatPercentWithCounts( + scientificState.signals.residual_syndrome_events, + scientificState.signals.syndrome_opportunities, + 4, + ), + request_line_count: formatCount(scientificState.signals.request_line_count), + response_line_count: formatCount(scientificState.signals.response_line_count), + expanded_shot_count: formatCompactCount(scientificState.signals.expanded_shot_count), + }; + }, [scientificOverheadProviderKind, scientificState.signals, scientificZeroBaseline]); + const scientificPrimaryCards = useMemo( + () => + SCIENTIFIC_PRIMARY_CARD_ORDER.map((key) => ({ + key, + contract: SCIENTIFIC_CARD_CONTRACTS[key], + availability: scientificState.metricAvailability[key], + })), + [scientificState.metricAvailability], + ); + const scientificSecondaryCards = useMemo( + () => + SCIENTIFIC_SECONDARY_CARD_ORDER.map((key) => ({ + key, + contract: SCIENTIFIC_CARD_CONTRACTS[key], + availability: scientificState.metricAvailability[key], + })), + [scientificState.metricAvailability], + ); + const scientificMissingPrimaryReasons = useMemo( + () => + scientificPrimaryCards + .filter((entry) => !entry.availability.available) + .map( + (entry) => + `${entry.contract.label} unavailable — ${entry.availability.availabilityReason}`, + ), + [scientificPrimaryCards], + ); + const scientificMissingSignalsLabel = useMemo(() => { + if (scientificState.state === "IDLE") { + return "No active scientific context"; + } + if (scientificState.completeness.missingSignals.length === 0) { + return "No missing scientific signals"; + } + return "Exact Calculation..."; + }, [scientificState.completeness.missingSignals.length, scientificState.state]); + const scientificIngestingRows = useMemo(() => { + const rows: Array<{ label: string; value: string }> = []; + if (!scientificState.hasRunContext) { + rows.push({ label: "Run", value: "No run selected" }); + } else { + const runLabel = activeRun?.id ? activeRun.id.slice(0, 8).toUpperCase() : "active"; + rows.push({ label: "Run", value: `Run ${runLabel} detected` }); + } + if (!scientificState.hasTelemetryContext) { + rows.push({ label: "Telemetry", value: "Awaiting decoder output" }); + } else { + rows.push({ label: "Telemetry", value: "Scientific telemetry stream connected" }); + } + if (scientificState.signals.rounds != null) { + rows.push({ + label: "Rounds", + value: `${scientificState.signals.rounds.toLocaleString()} rounds detected`, + }); + } + if (scientificState.signals.stabilizer_count != null) { + rows.push({ + label: "Stabilizers", + value: `${scientificState.signals.stabilizer_count.toLocaleString()} stabilizers tracked`, + }); + } + if (scientificState.signals.request_line_count != null) { + rows.push({ + label: "Request lines", + value: scientificState.signals.request_line_count.toLocaleString(), + }); + } + if (scientificState.signals.response_line_count != null) { + rows.push({ + label: "Response lines", + value: scientificState.signals.response_line_count.toLocaleString(), + }); + } + return rows; + }, [ + activeRun?.id, + scientificState.hasRunContext, + scientificState.hasTelemetryContext, + scientificState.signals.request_line_count, + scientificState.signals.response_line_count, + scientificState.signals.rounds, + scientificState.signals.stabilizer_count, + ]); + const scientificExactSourceRows = + runTelemetryScientific?.decoder_exact_metrics ?? activeRun?.metrics?.decoder_exact_metrics ?? []; + const scientificActiveDecoderRow = useMemo( + () => + scientificExactSourceRows.find( + (entry) => decoderRowMatchesActive(entry.decoder, activeDecoder) && entry.trials > 0, + ) ?? null, + [activeDecoder, scientificExactSourceRows], + ); + + useEffect(() => { + if (!import.meta.env.DEV) { + return; + } + const availableSignals = (Object.keys(SCIENTIFIC_FIELD_LABELS) as ScientificField[]).filter( + (field) => scientificState.signals[field] != null, + ); + console.debug("[scientific-summary]", { + selectedRunId: activeRunId, + activeDecoder, + activeDecoderExactRow: scientificActiveDecoderRow + ? { + decoder: scientificActiveDecoderRow.decoder, + trials: scientificActiveDecoderRow.trials, + logical_failures: scientificActiveDecoderRow.logical_failures, + } + : null, + exactRowsInScope: scientificExactSourceRows.length, + availableSignals, + scientificState: scientificState.state, + completenessPct: scientificState.completeness.percentage, + }); + }, [ + activeDecoder, + activeRunId, + scientificActiveDecoderRow, + scientificExactSourceRows.length, + scientificState.completeness.percentage, + scientificState.signals, + scientificState.state, + ]); + + const operationalKpiCards = useMemo(() => { + const cards: OperationalKpiCardModel[] = [ + { + key: "p95-latency", + label: "P95 Decoder Latency", + value: `${p95LatencyCurrent.toFixed(1)} ms`, + trendText: formatTrend(p95LatencyTrend), + trendDelta: p95LatencyTrend, + trendUpGood: false, + }, + { + key: "syndrome", + label: "Syndrome Satisfaction", + value: `${syndromeCurrent.toFixed(2)}%`, + trendText: formatTrend(syndromeTrend), + trendDelta: syndromeTrend, + trendUpGood: true, + }, + { + key: "throughput", + label: "Throughput", + value: `${throughputCurrent.toFixed(2)} ops/s`, + trendText: formatTrend(throughputTrend), + trendDelta: throughputTrend, + trendUpGood: true, + }, + { + key: "queue-age", + label: "Queue Age", + value: `${queueAgeMinutes.toFixed(1)} min`, + trendText: formatTrend(queueAgeTrend), + trendDelta: queueAgeTrend, + trendUpGood: false, + }, + { + key: "confidence", + label: "Confidence (Heuristic)", + value: `${confidenceScore.toFixed(1)}%`, + trendText: "Synthetic confidence from signal coverage", + trendDelta: 0, + trendUpGood: true, + }, + ]; + + if (globalRunLogicalErrorRate != null) { + cards.push({ + key: "global-run-ler", + label: "Global Run LER", + value: `${(globalRunLogicalErrorRate * 100).toFixed(4)}%`, + trendText: + previousGlobalRunLogicalErrorRate != null + ? `${formatTrend(globalRunLerTrend)} vs previous run` + : "Legacy run.metrics.logical_error_rate scope", + trendDelta: globalRunLerTrend, + trendUpGood: false, + }); + } + return cards; + }, [ + confidenceScore, + globalRunLerTrend, + globalRunLogicalErrorRate, + p95LatencyCurrent, + p95LatencyTrend, + previousGlobalRunLogicalErrorRate, + queueAgeMinutes, + queueAgeTrend, + syndromeCurrent, + syndromeTrend, + throughputCurrent, + throughputTrend, + ]); + const anomalyThresholds = { + noiseWarn: 0.022, + noiseCritical: 0.03, + successWarn: 95, + errorWarn: 5, + latencyWarn: 70, + perWarn: 1.2, + }; + + const noiseAnomalies = monitoringData.filter((point) => point.noise >= anomalyThresholds.noiseWarn); + const successAnomalies = monitoringData.filter((point) => point.success <= anomalyThresholds.successWarn); + const errorAnomalies = monitoringData.filter((point) => point.error >= anomalyThresholds.errorWarn); + const latencyAnomalies = monitoringData.filter((point) => point.latency >= anomalyThresholds.latencyWarn); + const physicalAnomalies = physicalNoiseData.filter( + (point) => point.physicalErrorPct >= anomalyThresholds.perWarn, + ); + const latestPhysicalPoint = + physicalNoiseData.length > 0 ? physicalNoiseData[physicalNoiseData.length - 1] : null; + const physicalPctCeiling = Number( + Math.max( + 1.4, + Math.ceil( + (Math.max( + anomalyThresholds.perWarn, + ...physicalNoiseData.map((point) => Math.max(point.physicalErrorPct, point.photonLossPct)), + ) + + 0.1) * + 10, + ) / 10, + ).toFixed(1), + ); + const physicalSigmaCeiling = Number( + Math.max( + 0.18, + Math.ceil((Math.max(0.01, ...physicalNoiseData.map((point) => point.displacementSigma)) + 0.01) * 20) / 20, + ).toFixed(2), + ); + const rawGkpOscillatorStates = runTelemetry?.gkp_oscillator_states ?? []; + const latestSyndromeRound = useMemo(() => { + if (syndromeSamples.length === 0) { + return null; + } + return syndromeSamples.reduce((roundMax, sample) => Math.max(roundMax, sample.round), 0); + }, [syndromeSamples]); + const latestRoundSyndromes = useMemo(() => { + if (latestSyndromeRound === null) { + return []; + } + return syndromeSamples.filter((sample) => sample.round === latestSyndromeRound); + }, [latestSyndromeRound, syndromeSamples]); + const syndromeExtractionActive = !systemOff && (activeSessionStreaming || activeRunStreaming); + const streamingStatusClass = systemOff + ? "status-failed" + : syndromeExtractionActive + ? "status-running" + : "status-warning"; + const streamingStatusLabel = systemOff + ? "Off" + : syndromeExtractionActive + ? activeIntegrationSession + ? sessionStatusLabel(activeIntegrationSession.status) + : "Streaming" + : "Standby"; + const extractionStatusLabel = systemOff + ? "Syndrome extraction off" + : syndromeExtractionActive + ? activeIntegrationSession + ? `Syndrome extraction ${sessionStatusLabel(activeIntegrationSession.status).toLowerCase()}` + : "Syndrome extraction live" + : "Syndrome extraction ended"; + + const qecLattice = useMemo(() => { + const stabilizerCount = Math.min(64, outerCodeDistance * outerCodeDistance); + const labelsFromSamples = Array.from( + new Set(latestRoundSyndromes.map((sample) => sample.stabilizer)), + ).sort((left, right) => { + const rankDelta = stabilizerSortKey(left) - stabilizerSortKey(right); + return rankDelta !== 0 ? rankDelta : left.localeCompare(right); + }); + const labels = labelsFromSamples.length > 0 ? [...labelsFromSamples] : []; + while (labels.length < stabilizerCount) { + labels.push(stabilizerLabel(labels.length)); + } + + const columns = outerCodeDistance; + const rows = outerCodeDistance; + const width = 620; + const height = 340; + const padX = 56; + const padY = 52; + const stepX = columns > 1 ? (width - padX * 2) / (columns - 1) : 0; + const stepY = rows > 1 ? (height - padY * 2) / (rows - 1) : 0; + const sampleByLabel = new Map( + latestRoundSyndromes.map((sample) => [sample.stabilizer, sample]), + ); + + const nodes: QecLatticeNode[] = labels.slice(0, stabilizerCount).map((label, index) => { + const row = Math.floor(index / columns); + const col = index % columns; + const sample = sampleByLabel.get(label); + return { + key: label, + label, + x: Number((padX + col * stepX).toFixed(2)), + y: Number((padY + row * stepY).toFixed(2)), + triggered: sample?.is_triggered ?? false, + value: sample?.value ?? 0, + }; + }); + + const edges: QecLatticeEdge[] = []; + nodes.forEach((node, index) => { + const row = Math.floor(index / columns); + const col = index % columns; + const rightIndex = index + 1; + if (col < columns - 1 && rightIndex < nodes.length) { + const right = nodes[rightIndex]; + edges.push({ + key: `${node.key}-${right.key}`, + x1: node.x, + y1: node.y, + x2: right.x, + y2: right.y, + }); + } + const downIndex = index + columns; + if (row < rows - 1 && downIndex < nodes.length) { + const down = nodes[downIndex]; + edges.push({ + key: `${node.key}-${down.key}`, + x1: node.x, + y1: node.y, + x2: down.x, + y2: down.y, + }); + } + }); + + return { + nodes, + edges, + columns, + rows, + }; + }, [latestRoundSyndromes, outerCodeDistance]); + const qecTriggeredNodes = qecLattice.nodes.filter((node) => node.triggered); + const qecPointerNodes = qecTriggeredNodes.slice(0, 8); + const qecStabilizerTotal = Math.max(0, runTelemetry?.stabilizer_count ?? qecLattice.nodes.length); + const qecTrackedTotal = qecLattice.nodes.length; + const qecMapTrimmed = qecStabilizerTotal > qecTrackedTotal; + const qecTriggeredPct = + qecTrackedTotal > 0 ? (qecTriggeredNodes.length / Math.max(1, qecTrackedTotal)) * 100 : 0; + const qecSurfaceDistance = outerCodeDistance; + const qecSurfaceRoundLabel = latestSyndromeRound !== null ? `Round ${latestSyndromeRound + 1}` : "No round"; + const gkpOscillatorStates = useMemo(() => { + if (rawGkpOscillatorStates.length > 0) { + return rawGkpOscillatorStates; + } + if (physicalNoiseData.length === 0) { + return []; + } + const sliced = physicalNoiseData.slice(-Math.min(48, physicalNoiseData.length)); + return sliced.map((point, index) => { + const angle = index * 0.58 + point.round * 0.12; + const radius = clamp(point.displacementSigma * 3.2, 0.16, 1.25); + const q = Number((radius * Math.cos(angle)).toFixed(4)); + const p = Number((radius * Math.sin(angle)).toFixed(4)); + const variance = Number((0.03 + point.displacementSigma * 0.24).toFixed(4)); + const energy = Number((point.photonLossPct / 9).toFixed(4)); + return { + round: point.round - 1, + mode: `M${((index % 12) + 1).toString().padStart(2, "0")}`, + q, + p, + variance, + energy, + flagged: point.physicalErrorPct >= anomalyThresholds.perWarn || point.photonLossPct >= 1.1, + }; + }); + }, [anomalyThresholds.perWarn, physicalNoiseData, rawGkpOscillatorStates]); + const gkpOscillatorFallback = rawGkpOscillatorStates.length === 0 && gkpOscillatorStates.length > 0; + const gkpAxisLimit = useMemo(() => { + if (gkpOscillatorStates.length === 0) { + return 1; + } + const maxAbs = gkpOscillatorStates.reduce( + (currentMax, sample) => Math.max(currentMax, Math.abs(sample.q), Math.abs(sample.p)), + 0, + ); + return Math.max(0.95, Math.min(1.8, Math.ceil(maxAbs * 10) / 10)); + }, [gkpOscillatorStates]); + const gkpOscillatorMapPoints = useMemo(() => { + if (gkpOscillatorStates.length === 0) { + return []; + } + const width = 620; + const height = 340; + const centerX = width / 2; + const centerY = height / 2; + const scaleX = 240; + const scaleY = 124; + return gkpOscillatorStates.slice(-180).map((sample, index) => { + const x = Number((centerX + (sample.q / gkpAxisLimit) * scaleX).toFixed(2)); + const y = Number((centerY - (sample.p / gkpAxisLimit) * scaleY).toFixed(2)); + return { + key: `${sample.round}-${sample.mode}-${index}`, + mode: sample.mode, + round: sample.round, + q: sample.q, + p: sample.p, + variance: sample.variance ?? 0, + energy: sample.energy ?? 0, + flagged: sample.flagged ?? false, + x, + y, + }; + }); + }, [gkpAxisLimit, gkpOscillatorStates]); + const gkpFlaggedPoints = gkpOscillatorMapPoints.filter((point) => point.flagged); + const gkpPointerPoints = gkpFlaggedPoints.slice(0, 10); + const gkpModesTracked = new Set(gkpOscillatorMapPoints.map((point) => point.mode)).size; + const gkpLatestRound = gkpOscillatorStates.reduce((roundMax, sample) => Math.max(roundMax, sample.round), -1); + const gkpRoundLabel = gkpLatestRound >= 0 ? `Round ${gkpLatestRound + 1}` : "No round"; + const gkpVarianceAvg = + gkpOscillatorMapPoints.length > 0 + ? average(gkpOscillatorMapPoints.map((point) => point.variance)) + : 0; + const gkpEnergyAvg = + gkpOscillatorMapPoints.length > 0 ? average(gkpOscillatorMapPoints.map((point) => point.energy)) : 0; + const qecMapHeading = + encodingMapMode === "surface" + ? "Surface Syndrome Map (Outer Code)" + : "Raw GKP Oscillator Map (Inner Code)"; + const qecMapSubtitle = + encodingMapMode === "surface" + ? `Surface lattice telemetry for ${qecSurfaceRoundLabel} · distance-${qecSurfaceDistance}` + : gkpOscillatorFallback + ? `Derived oscillator projection for ${gkpRoundLabel} from physical noise telemetry` + : `Direct oscillator telemetry for ${gkpRoundLabel} with phase-space state vectors`; + const opsInterventionSeries = useMemo(() => { + const rows = (runTelemetry?.decoder_interventions ?? []).slice(-32); + if (rows.length === 0) { + return Array.from({ length: 12 }, (_, index) => ({ + key: `idle-${index}`, + roundLabel: `R${index + 1}`, + totalFlips: 0, + totalResidual: 0, + loadIndex: 0, + round: null as number | null, + })); + } + const byRound = new Map(); + rows.forEach((row) => { + const current = byRound.get(row.round) ?? { flips: 0, residual: 0 }; + current.flips += row.flips; + current.residual += row.residual_weight; + byRound.set(row.round, current); + }); + const ordered = Array.from(byRound.entries()) + .sort((left, right) => left[0] - right[0]) + .slice(-12); + const peak = Math.max(1, ...ordered.map(([, value]) => value.flips + value.residual * 1.4)); + return ordered.map(([round, value]) => { + const combined = value.flips + value.residual * 1.4; + return { + key: `round-${round}`, + roundLabel: `R${round + 1}`, + totalFlips: Number(value.flips.toFixed(2)), + totalResidual: Number(value.residual.toFixed(2)), + loadIndex: Number(((combined / peak) * 100).toFixed(1)), + round, + }; + }); + }, [runTelemetry?.decoder_interventions]); + const latestInterventionRoundLabel = useMemo(() => { + const latest = opsInterventionSeries[opsInterventionSeries.length - 1]; + if (!latest || latest.round === null) { + return "idle"; + } + return `R${latest.round + 1}`; + }, [opsInterventionSeries]); + const opsRawEvents = useMemo(() => { + if (systemOff) { + return [ + { + level: "warn" as const, + text: "System off: telemetry and decoder stream halted.", + tag: "off", + source: "system" as const, + }, + ]; + } + const events: Array<{ + level: "info" | "warn" | "critical" | "ok"; + text: string; + tag: string; + source: "decoder" | "physical" | "scope" | "system"; + }> = []; + + if (activeIntegrationSession) { + events.push({ + level: sessionStatusLevel(activeIntegrationSession.status), + text: `Session #${activeIntegrationSession.id.slice(0, 8).toUpperCase()} ${sessionStatusLabel( + activeIntegrationSession.status, + )} (${activeIntegrationSession.adapter_id})`, + tag: `${activeIntegrationSession.provider.toUpperCase()} · ${formatAgo( + activeIntegrationSession.updated_at, + )}`, + source: "system", + }); + } + + (sessionLogsQuery.data?.lines ?? []).slice(-30).forEach((entry) => { + events.push({ + level: inferLogLevel(entry.stream, entry.line), + text: entry.line.trim() || "(empty line)", + tag: `${entry.stream.toUpperCase()} · ${formatClock(entry.timestamp)}`, + source: "system", + }); + }); + + (runTelemetry?.decoder_interventions ?? []) + .slice(-28) + .forEach((row) => { + const decoder = decoderLabel(parseDecoderKey(row.decoder) ?? activeDecoder); + const level = row.residual_weight >= 4 ? "warn" : "ok"; + events.push({ + level, + text: `Round ${row.round + 1}: ${decoder} flips=${row.flips}, residual=${row.residual_weight}`, + tag: `R${row.round + 1}`, + source: "decoder", + }); + }); + + physicalAnomalies.slice(-8).forEach((point) => { + events.push({ + level: "critical", + text: `Physical anomaly at round ${point.round}: PER ${point.physicalErrorPct.toFixed(3)}%`, + tag: "PER", + source: "physical", + }); + }); + + activityFeed.forEach((item) => { + events.push({ + level: item.tone === "red" ? "critical" : item.tone === "blue" ? "info" : "ok", + text: item.text, + tag: item.time, + source: "scope", + }); + }); + + if (events.length === 0) { + events.push({ + level: "info", + text: "Waiting for decoder telemetry stream...", + tag: "idle", + source: "system", + }); + } + return events.slice(-60); + }, [ + activeDecoder, + activeIntegrationSession, + activityFeed, + physicalAnomalies, + runTelemetry?.decoder_interventions, + sessionLogsQuery.data?.lines, + systemOff, + ]); + const opsLiveEvents = useMemo(() => { + const maxRows = Math.min(12, opsRawEvents.length); + if (maxRows === 0) { + return []; + } + return Array.from({ length: maxRows }, (_, index) => { + const offset = (opsLogCursor + index) % opsRawEvents.length; + const event = opsRawEvents[offset]; + return { + ...event, + id: `${event.source}-${event.tag}-${offset}-${index}`, + }; + }); + }, [opsLogCursor, opsRawEvents]); + + useEffect(() => { + if (opsRawEvents.length <= 1) { + return; + } + const delay = syndromeExtractionActive ? 900 : 1700; + const timer = window.setInterval(() => { + setOpsLogCursor((current) => (current + 1) % opsRawEvents.length); + }, delay); + return () => window.clearInterval(timer); + }, [opsRawEvents.length, syndromeExtractionActive]); + + const compareDelta = useMemo(() => { + if (!compareMode) { + return 0; + } + const primaryValues = monitoringData.map((point) => metricValue(point, activeChart, false)); + const compareValues = monitoringData.map((point) => metricValue(point, activeChart, true)); + return percentDelta(average(primaryValues), average(compareValues)); + }, [activeChart, compareMode, monitoringData]); + const compareHigherIsBetter = activeChart === "success"; + const compareIsGood = compareHigherIsBetter ? compareDelta >= 0 : compareDelta <= 0; + + const workflowAlerts = useMemo(() => { + const items: WorkflowAlertItem[] = []; + if (anyApiError) { + items.push({ + id: "api-link", + level: "critical", + title: "Backend Connectivity", + detail: "One or more API endpoints are unreachable in live mode.", + metric: "API health", + }); + } + if (latencyAnomalies.length > 0) { + items.push({ + id: "latency-spike", + level: "warning", + title: "Latency threshold breached", + detail: `${latencyAnomalies.length} windows above ${anomalyThresholds.latencyWarn} ms`, + metric: `P95 ${p95LatencyCurrent.toFixed(1)} ms`, + }); + } + if (errorAnomalies.length > 0) { + items.push({ + id: "error-rise", + level: "warning", + title: "Decoder error rate elevated", + detail: `${errorAnomalies.length} windows above ${anomalyThresholds.errorWarn}%`, + metric: `${average(errorAnomalies.map((point) => point.error)).toFixed(2)}% avg`, + }); + } + if (perValue !== null && perValue >= anomalyThresholds.perWarn) { + items.push({ + id: "per-high", + level: "critical", + title: "Physical error rate high", + detail: `PER exceeded ${anomalyThresholds.perWarn.toFixed(1)}% threshold`, + metric: `${perValue.toFixed(3)}%`, + }); + } + if (items.length === 0) { + items.push({ + id: "all-clear", + level: "info", + title: "No critical anomalies", + detail: "All monitored bands are within configured thresholds.", + metric: "Operational", + }); + } + return items; + }, [ + anomalyThresholds.errorWarn, + anomalyThresholds.latencyWarn, + anomalyThresholds.perWarn, + anyApiError, + errorAnomalies, + latencyAnomalies, + p95LatencyCurrent, + perValue, + ]); + + const updateWorkflowState = (alertId: string, patch: Partial) => { + setAlertWorkflow((current) => { + const previous = current[alertId] ?? { + acknowledged: false, + owner: "Unassigned", + notes: "", + }; + return { + ...current, + [alertId]: { + ...previous, + ...patch, + }, + }; + }); + }; + + const activeProviderName = + providerFilter !== "all" + ? providerById.get(providerFilter)?.name ?? "Unknown Provider" + : "All Providers"; + const activeRunProvider = activeRun ? providerById.get(activeRun.provider_id) ?? null : null; + const heroProviderLabel = activeRunProvider?.name ?? activeProviderName; + const heroHardwareLabel = activeRunProvider + ? providerKindLabel(activeRunProvider.kind) + : "Mixed Hardware Scope"; + const activeRunShortId = activeRunId ? activeRunId.slice(0, 8).toUpperCase() : "None"; + const activeSessionShortId = activeIntegrationSession + ? activeIntegrationSession.id.slice(0, 8).toUpperCase() + : "None"; + const quickLaunchBusy = createRunMutation.isPending || createSessionMutation.isPending; + const sessionStopBusy = stopSessionMutation.isPending; + const launchProviderFamily = launchProvider ? resolveProviderFamily(launchProvider) : "unknown"; + const quickLaunchRequiresNeuralModel = + launchProvider != null && + activeDecoder === "neural_mwpm" && + familyRequiresNeuralModel(launchProviderFamily); + const quickLaunchMissingNeuralModel = quickLaunchRequiresNeuralModel && !neuralModelPath.trim(); + const replaySourceOptions = useMemo(() => { + return [...scopedRuns] + .filter((run) => run.status === "finished") + .sort((left, right) => new Date(right.updated_at).getTime() - new Date(left.updated_at).getTime()) + .map((run) => ({ + runId: run.id, + datasetLabel: run.dataset_label, + providerName: providerById.get(run.provider_id)?.name ?? run.provider_id, + updatedAtLabel: formatAgo(run.updated_at), + })); + }, [providerById, scopedRuns]); + useEffect(() => { + if (replaySourceOptions.length === 0) { + if (replaySourceRunId !== "") { + setReplaySourceRunId(""); + } + return; + } + if (!replaySourceRunId || !replaySourceOptions.some((source) => source.runId === replaySourceRunId)) { + setReplaySourceRunId(replaySourceOptions[0].runId); + } + }, [replaySourceOptions, replaySourceRunId]); + const selectedReplaySourceRun = useMemo( + () => (replaySourceRunId ? runById.get(replaySourceRunId) ?? null : null), + [replaySourceRunId, runById], + ); + const replayLaunchProvider = selectedReplaySourceRun + ? providerById.get(selectedReplaySourceRun.provider_id) ?? null + : launchProvider; + const replayLaunchProviderFamily = replayLaunchProvider ? resolveProviderFamily(replayLaunchProvider) : "unknown"; + const benchmarkIncludesNeural = benchmarkDecoders.includes("neural_mwpm"); + const benchmarkRequiresNeuralModel = + launchProvider != null && + benchmarkIncludesNeural && + familyRequiresNeuralModel(launchProviderFamily); + const providerOperationalStateText = !launchProvider + ? "No provider configured" + : !providerReady(launchProvider) + ? "Provider configured but currently offline" + : scientificTransport(launchProvider) != null + ? "Provider available and scientific-ready" + : "Provider configured but scientific mode unsupported"; + const baseSessionUnavailableReason = + !isApi + ? "Scientific session unavailable — switch to Live API mode" + : systemOff + ? "Scientific session unavailable — system is off" + : anyApiError + ? "Scientific session unavailable — backend unreachable" + : launchProvider == null + ? "Scientific session unavailable — no provider configured" + : !providerReady(launchProvider) + ? "Scientific session unavailable — provider configured but offline" + : !launchProvider.supports_scientific + ? "Scientific session unavailable — provider configured but scientific mode unsupported" + : scientificTransport(launchProvider) == null + ? "Scientific session unavailable — provider configured but live mode unsupported" + : launchProviderFamily === "unknown" + ? "Scientific session unavailable — provider configured but adapter mapping missing" + : null; + const benchmarkBaseUnavailableReason = + !isApi + ? "Benchmark unavailable — switch to Live API mode" + : systemOff + ? "Benchmark unavailable — system is off" + : anyApiError + ? "Benchmark unavailable — backend unreachable" + : launchProvider == null + ? "Benchmark unavailable — no provider configured" + : !providerReady(launchProvider) + ? "Benchmark unavailable — provider configured but offline" + : !launchProvider.supports_benchmark + ? "Benchmark unavailable — provider configured but benchmark mode unsupported" + : scientificTransport(launchProvider) == null + ? "Benchmark unavailable — provider configured but live mode unsupported" + : launchProviderFamily === "unknown" + ? "Benchmark unavailable — provider configured but adapter mapping missing" + : null; + const replayBaseUnavailableReason = + !isApi + ? "Replay unavailable — switch to Live API mode" + : systemOff + ? "Replay unavailable — system is off" + : anyApiError + ? "Replay unavailable — backend unreachable" + : replayLaunchProvider == null + ? "Replay unavailable — no provider configured" + : !providerReady(replayLaunchProvider) + ? "Replay unavailable — provider configured but offline" + : !replayLaunchProvider.supports_replay + ? "Replay unavailable — provider configured but replay mode unsupported" + : replayLaunchProviderFamily === "unknown" + ? "Replay unavailable — provider configured but adapter mapping missing" + : null; + const scientificSessionUnavailableReason = + baseSessionUnavailableReason ?? + (quickLaunchMissingNeuralModel + ? "Scientific session unavailable — set Neural Model path for Neural MWPM replay sessions" + : null); + const benchmarkMenuDisabledReason = + benchmarkBaseUnavailableReason ?? + (benchmarkRequiresNeuralModel && !neuralModelPath.trim() + ? "Benchmark unavailable — set Neural Model path for Neural MWPM replay sessions" + : null); + const benchmarkSessionUnavailableReason = + benchmarkMenuDisabledReason ?? + (benchmarkDecoders.length === 0 ? "Benchmark unavailable — select at least one decoder" : null); + const replaySessionUnavailableReason = + replayBaseUnavailableReason ?? + (replaySourceOptions.length === 0 + ? providersData.length > 0 + ? "Replay unavailable — provider configured but no replay source available" + : "Replay unavailable — no historical run in scope" + : null) ?? + (!selectedReplaySourceRun ? "Replay unavailable — select a replay source" : null); + const sessionRunning = activeSessionStreaming; + const launcherStatus = sessionStopBusy + ? "stopping" + : quickLaunchBusy || sessionControlState.status === "launching" + ? "launching" + : sessionRunning + ? "running" + : sessionControlState.status === "failed" + ? "failed" + : "idle"; + const drilldownProviderName = activeRun + ? providerById.get(activeRun.provider_id)?.name ?? activeRun.provider_id + : activeProviderName; + + const queueIbmApiKeyLaunch = (input: LaunchSessionInput) => { + setPendingIbmLaunch(input); + setIbmApiKeyInput(""); + setIbmLiveSourceMode(input.ibmLiveSourceMode ?? "metadata"); + setIbmInstanceInput(input.ibmInstance ?? ""); + setIbmApiKeyError(null); + setIbmApiKeyDialogOpen(true); + setSessionLauncherMenuOpen(false); + setBenchmarkDialogOpen(false); + setReplayDialogOpen(false); + setQuickLaunchTone("info"); + setQuickLaunchMessage("Enter IBM API key to start IBM live session."); + }; + + const closeIbmApiKeyDialog = () => { + if (setIbmApiKeyMutation.isPending) { + return; + } + setIbmApiKeyDialogOpen(false); + setPendingIbmLaunch(null); + setIbmApiKeyInput(""); + setIbmApiKeyError(null); + }; + + const launchSession = async (input: LaunchSessionInput) => { + if (systemOff) { + setSystemOff(false); + } + armSystem(); + clearError(); + beginLaunch(input.mode); + setSessionLauncherMenuOpen(false); + setBenchmarkDialogOpen(false); + setReplayDialogOpen(false); + setActiveHomeSessionSnapshot(null); + homeSessionStatusRef.current = null; + const primaryDecoder = input.decoders[0] ?? activeDecoder; + const launchPlan = buildQuickLaunchPlan(input.provider, primaryDecoder, neuralModelPath, input.mode); + const launchProviderFamily = resolveProviderFamily(input.provider); + const launchWorkflowId = workflowForProviderFamily(launchProviderFamily); + const launchDecoders = input.decoders.length > 0 ? input.decoders : [activeDecoder]; + const launchDecoderLabel = launchDecoders.map((decoder) => decoderLabel(decoder)).join(", "); + if (!launchPlan) { + const unsupportedMessage = + input.mode === "replay" + ? "Replay unavailable — provider configured but replay mode unsupported" + : "Session unavailable — provider configured but live mode unsupported"; + markFailed(unsupportedMessage); + setQuickLaunchTone("error"); + setQuickLaunchMessage(unsupportedMessage); + return; + } + if ( + adapterRequiresNeuralModel(launchPlan.adapterId) && + launchDecoders.includes("neural_mwpm") && + !neuralModelPath.trim() + ) { + markFailed("Set Neural Model path before running neural_mwpm in replay mode."); + setQuickLaunchTone("error"); + setQuickLaunchMessage("Set Neural Model path before running neural_mwpm in replay mode."); + return; + } + const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19); + const modeLabel = + input.mode === "scientific" + ? "scientific" + : input.mode === "benchmark" + ? "benchmark" + : "replay"; + const sourceLabel = input.runSource ? ` from ${input.runSource.id.slice(0, 8).toUpperCase()}` : ""; + const circuitLabel = input.circuitDesign ? ` using ${input.circuitDesign.name}` : ""; + setQuickLaunchTone("info"); + setQuickLaunchMessage( + `Starting ${input.provider.name} ${modeLabel} session${sourceLabel} with ${launchDecoderLabel}${circuitLabel}...`, + ); + setActiveContext({ runId: null, sessionId: null, mode: input.mode }); + let createdRunId: string | null = null; + + try { + const run = await createRunMutation.mutateAsync({ + workflow_id: launchWorkflowId, + provider_id: input.provider.id, + dataset_label: `${input.provider.name} ${input.datasetHint} ${modeLabel}${sourceLabel} ${timestamp}`, + decoders: launchDecoders, + }); + createdRunId = run.id; + setActiveContext({ runId: run.id, sessionId: null, mode: input.mode }); + const allowCircuitDesignConfig = input.circuitDesign && supportsSoftwareCircuitDesign(input.provider); + const circuitDesign = allowCircuitDesignConfig ? input.circuitDesign : null; + const circuitConfig = circuitDesign + ? { + circuit_name: circuitDesign.name, + circuit_qasm: circuitDesign.qasm, + circuit_qubits: circuitDesign.qubitCount, + circuit_depth: circuitDesign.depth, + circuit_gate_count: circuitDesign.gateCount, + circuit_hardware_target: circuitDesign.hardwareTarget, + circuit_detector_model: circuitDesign.compileArtifact.photonic_detector_model, + circuit_noise_config: JSON.stringify(circuitDesign.noiseConfig), + circuit_compile_artifact: JSON.stringify(circuitDesign.compileArtifact), + circuit_calibration_snapshot: + circuitDesign.calibrationSnapshotId ?? circuitDesign.compileArtifact.calibration_snapshot_id, + simulator_shots: 16384, + circuit_gate_plan: JSON.stringify( + circuitDesign.operations.map((operation) => ({ + gate: operation.gate, + target: operation.target, + control: operation.control ?? null, + parameter: operation.parameter ?? null, + })), + ), + } + : {}; + const session = await createSessionMutation.mutateAsync({ + run_id: run.id, + adapter_id: launchPlan.adapterId, + config: { + ...launchPlan.config, + mode: input.mode, + provider_scope: providerFilter, + time_range: timeRangeFilter, + run_source: input.runSource?.id, + compare_decoders: input.mode === "benchmark" ? launchDecoders : undefined, + skip_replay: input.mode === "replay" ? false : launchPlan.config.skip_replay, + ...circuitConfig, + ...(launchPlan.adapterId === "ibm_superconducting_live" + ? { + ibm_live_source_mode: input.ibmLiveSourceMode ?? "metadata", + ibm_instance: + input.ibmInstance && input.ibmInstance.trim().length > 0 + ? input.ibmInstance.trim() + : undefined, + } + : {}), + }, + }); + markRunning({ runId: run.id, sessionId: session.id, mode: input.mode }); + setActiveHomeSessionSnapshot(session); + setOpsLogCursor(0); + setQuickLaunchTone("success"); + setQuickLaunchMessage( + `Session #${session.id.slice(0, 8).toUpperCase()} started for ${input.provider.name} (${launchDecoderLabel})${circuitLabel}.`, + ); + } catch (error) { + if (error instanceof ApiError && error.status === 404) { + markFailed("Integration session endpoint is unavailable."); + setQuickLaunchTone("error"); + setQuickLaunchMessage( + "Integration session endpoint is unavailable. Restart backend from the latest source.", + ); + setActiveHomeSessionSnapshot(null); + return; + } + const message = error instanceof Error ? error.message : "Failed to start session."; + markFailed(message); + setQuickLaunchTone("error"); + setQuickLaunchMessage(message); + setActiveContext({ runId: createdRunId, sessionId: null, mode: input.mode }); + setActiveHomeSessionSnapshot(null); + } + }; + + const launchSessionWithProviderPrompt = async (input: LaunchSessionInput) => { + const providerFamily = resolveProviderFamily(input.provider); + if (providerFamily === "ibm" && (input.mode === "scientific" || input.mode === "benchmark")) { + queueIbmApiKeyLaunch(input); + return; + } + await launchSession(input); + }; + + const handleConfirmIbmApiKeyAndLaunch = async () => { + if (!pendingIbmLaunch) { + setIbmApiKeyDialogOpen(false); + return; + } + const trimmedKey = ibmApiKeyInput.trim(); + if (!trimmedKey) { + const message = "IBM API key is required to start IBM live sessions."; + setIbmApiKeyError(message); + setQuickLaunchTone("error"); + setQuickLaunchMessage(message); + return; + } + const trimmedInstance = ibmInstanceInput.trim(); + try { + setIbmApiKeyError(null); + await setIbmApiKeyMutation.mutateAsync({ api_key: trimmedKey }); + const queuedLaunch = { + ...pendingIbmLaunch, + ibmLiveSourceMode, + ibmInstance: trimmedInstance || undefined, + }; + setPendingIbmLaunch(null); + setIbmApiKeyDialogOpen(false); + setIbmApiKeyInput(""); + setQuickLaunchTone("info"); + setQuickLaunchMessage("IBM API key stored. Starting session..."); + await launchSession(queuedLaunch); + } catch (error) { + const message = error instanceof Error ? error.message : "Failed to store IBM API key."; + setIbmApiKeyError(message); + setQuickLaunchTone("error"); + setQuickLaunchMessage(message); + } + }; + + const openCircuitDesignDialogForLaunch = (input: LaunchSessionInput) => { + setPendingCircuitLaunch(input); + setCircuitDesignDialogOpen(true); + setSessionLauncherMenuOpen(false); + setBenchmarkDialogOpen(false); + setReplayDialogOpen(false); + }; + + const closeCircuitDesignDialog = () => { + if (quickLaunchBusy) { + return; + } + setCircuitDesignDialogOpen(false); + setPendingCircuitLaunch(null); + }; + + const handleStartSessionFromCircuitDesign = async (circuitDesign: CircuitDesignDraft) => { + if (!pendingCircuitLaunch) { + return; + } + const launchInput: LaunchSessionInput = { + ...pendingCircuitLaunch, + circuitDesign, + datasetHint: `${pendingCircuitLaunch.datasetHint} ${circuitDesign.name}`, + }; + setCircuitDesignDialogOpen(false); + setPendingCircuitLaunch(null); + await launchSessionWithProviderPrompt(launchInput); + }; + + const handleStartScientificSession = async () => { + if (scientificSessionUnavailableReason) { + setQuickLaunchTone("error"); + setQuickLaunchMessage(scientificSessionUnavailableReason); + return; + } + if (!launchProvider) { + return; + } + const launchInput: LaunchSessionInput = { + mode: "scientific", + provider: launchProvider, + decoders: [activeDecoder], + datasetHint: `scientific ${timeRangeFilter} ${activeDecoder}`, + }; + if (supportsSoftwareCircuitDesign(launchProvider)) { + openCircuitDesignDialogForLaunch(launchInput); + return; + } + await launchSessionWithProviderPrompt(launchInput); + }; + + const handleStartBenchmarkSession = async () => { + if (benchmarkSessionUnavailableReason) { + setQuickLaunchTone("error"); + setQuickLaunchMessage(benchmarkSessionUnavailableReason); + return; + } + if (!launchProvider || benchmarkDecoders.length === 0) { + return; + } + await launchSessionWithProviderPrompt({ + mode: "benchmark", + provider: launchProvider, + decoders: benchmarkDecoders, + datasetHint: `benchmark ${timeRangeFilter}`, + }); + }; + + const handleStartReplaySession = async () => { + if (replaySessionUnavailableReason) { + setQuickLaunchTone("error"); + setQuickLaunchMessage(replaySessionUnavailableReason); + return; + } + if (!selectedReplaySourceRun) { + return; + } + const sourceProvider = providerById.get(selectedReplaySourceRun.provider_id); + if (!sourceProvider) { + setQuickLaunchTone("error"); + setQuickLaunchMessage("Replay unavailable — source provider is no longer configured."); + return; + } + if (!providerReady(sourceProvider)) { + setQuickLaunchTone("error"); + setQuickLaunchMessage("Replay unavailable — source provider is offline."); + return; + } + if (!sourceProvider.supports_replay) { + setQuickLaunchTone("error"); + setQuickLaunchMessage("Replay unavailable — source provider does not support replay mode."); + return; + } + await launchSession({ + mode: "replay", + provider: sourceProvider, + decoders: selectedReplaySourceRun.decoders.length > 0 + ? (selectedReplaySourceRun.decoders + .map((decoder) => parseDecoderKey(decoder)) + .filter((decoder): decoder is DecoderKey => decoder !== null)) + : [activeDecoder], + datasetHint: `replay source ${selectedReplaySourceRun.id.slice(0, 8).toUpperCase()}`, + runSource: selectedReplaySourceRun, + }); + }; + + const handleStopSession = async () => { + const sessionId = activeIntegrationSession?.id ?? activeHomeSessionId; + if (!sessionId) { + setQuickLaunchTone("error"); + setQuickLaunchMessage("No active session to stop."); + return; + } + markStopping(); + setQuickLaunchTone("info"); + setQuickLaunchMessage(`Stopping session #${sessionId.slice(0, 8).toUpperCase()}...`); + try { + await stopSessionMutation.mutateAsync(sessionId); + markStopped({ preserveRun: true }); + setActiveHomeSessionSnapshot(null); + homeSessionStatusRef.current = null; + setQuickLaunchTone("info"); + setQuickLaunchMessage(`Session #${sessionId.slice(0, 8).toUpperCase()} stopped.`); + } catch (error) { + const message = error instanceof Error ? error.message : "Failed to stop active session."; + markFailed(message); + setQuickLaunchTone("error"); + setQuickLaunchMessage(message); + } + }; + const handleToggleBenchmarkDecoder = (decoder: DecoderKey) => { + setBenchmarkDecoders((current) => + current.includes(decoder) + ? current.filter((value) => value !== decoder) + : [...current, decoder], + ); + }; + const handleOpenBenchmarkDialog = () => { + setSessionLauncherMenuOpen(false); + setBenchmarkDialogOpen(true); + }; + const handleOpenReplayDialog = () => { + setSessionLauncherMenuOpen(false); + setReplayDialogOpen(true); + }; + const handleViewRun = () => { + if (!activeRunId) { + return; + } + navigate("/runs"); + }; + const handleOpenTelemetryForRun = () => { + if (!activeRunId) { + return; + } + navigate(`/decoder/telemetry?runA=${encodeURIComponent(activeRunId)}&compare=0`); + }; + const handleOpenValidationForRun = () => { + if (!activeRunId) { + return; + } + navigate(`/decoder/validation?run=${encodeURIComponent(activeRunId)}`); + }; + + const handleSystemOff = async () => { + if (systemOff) { + setSystemOff(false); + setQuickLaunchTone("info"); + setQuickLaunchMessage("System turned on in standby. Select a provider and start a run to populate metrics."); + return; + } + + setSystemOff(true); + setDrilldown(null); + setOpsLogCursor(0); + setSessionLauncherMenuOpen(false); + setCircuitDesignDialogOpen(false); + setPendingCircuitLaunch(null); + setBenchmarkDialogOpen(false); + setReplayDialogOpen(false); + markStopped(); + setActiveContext({ runId: null, sessionId: null, mode: null }); + setActiveHomeSessionSnapshot(null); + homeSessionStatusRef.current = null; + setQuickLaunchTone("info"); + const sessionId = activeIntegrationSession?.id ?? activeHomeSessionId; + if (apiEnabled && sessionId) { + try { + await stopSessionMutation.mutateAsync(sessionId); + setQuickLaunchMessage( + `System switched off. Session #${sessionId.slice(0, 8).toUpperCase()} stopped and metrics reset to zero.`, + ); + return; + } catch (error) { + const message = error instanceof Error ? error.message : "Failed to stop active session while powering off."; + setQuickLaunchTone("error"); + setQuickLaunchMessage(`System switched off, but session stop failed: ${message}`); + return; + } + } + setQuickLaunchMessage("System switched off. Metrics reset to zero."); + }; + + const handleRealtimeDrilldown = (event: unknown) => { + const payload = extractChartPayload(event); + if (!payload) { + return; + } + + const primaryValue = metricValue(payload, activeChart, false); + const compareValue = metricValue(payload, activeChart, true); + const hasCompare = compareMode && Number.isFinite(compareValue); + const interventionTimeline = + runTelemetry?.decoder_interventions + .slice(-4) + .map( + (row) => + `Round ${row.round + 1}: ${row.decoder} flips=${row.flips}, residual=${row.residual_weight}`, + ) ?? []; + + setDrilldown({ + source: "realtime", + title: `${chartLabel(activeChart)} @ ${payload.slot}`, + summary: `${decoderLabel(activeDecoder)} ${ + activeChart === "latency" ? `${primaryValue.toFixed(1)} ms` : `${primaryValue.toFixed(2)}` + }`, + keyValues: [ + { label: "Provider", value: drilldownProviderName }, + { label: "Run", value: activeRunId ?? "N/A" }, + { label: "Decoder", value: decoderLabel(activeDecoder) }, + { + label: "Primary value", + value: + activeChart === "latency" + ? `${primaryValue.toFixed(1)} ms` + : activeChart === "noise" + ? `${(primaryValue * 100).toFixed(2)}%` + : `${primaryValue.toFixed(2)}%`, + }, + { + label: "Compare value", + value: hasCompare + ? activeChart === "latency" + ? `${compareValue.toFixed(1)} ms` + : activeChart === "noise" + ? `${(compareValue * 100).toFixed(2)}%` + : `${compareValue.toFixed(2)}%` + : "N/A", + }, + ], + timeline: + interventionTimeline.length > 0 + ? interventionTimeline + : activityFeed.slice(0, 4).map((item) => `${item.time}: ${item.text}`), + }); + }; + + const handlePhysicalDrilldown = (event: unknown) => { + const payload = extractChartPayload(event); + if (!payload) { + return; + } + const roundInterventions = + runTelemetry?.decoder_interventions + .filter((row) => row.round === payload.round - 1) + .map( + (row) => + `${decoderLabel(parseDecoderKey(row.decoder) ?? activeDecoder)} flips=${row.flips}, residual=${ + row.residual_weight + }`, + ) ?? []; + + setDrilldown({ + source: "physical", + title: `Physical Channel Round ${payload.round}`, + summary: `PER ${payload.physicalErrorPct.toFixed(3)}% | Photon Loss ${payload.photonLossPct.toFixed(3)}%`, + keyValues: [ + { label: "Provider", value: drilldownProviderName }, + { label: "Run", value: activeRunId ?? "N/A" }, + { label: "Round", value: payload.round.toString() }, + { label: "Physical Error Rate", value: `${payload.physicalErrorPct.toFixed(3)}%` }, + { label: "Photon Loss Rate", value: `${payload.photonLossPct.toFixed(3)}%` }, + { label: "Displacement Sigma", value: payload.displacementSigma.toFixed(4) }, + ], + timeline: + roundInterventions.length > 0 + ? roundInterventions + : ["No decoder interventions recorded for this round."], + }); + }; + + const renderInterpretationPanel = () => ( +
+
+
Realtime Decoder Console
+ + ● {streamingStatusLabel} + +
+
Intervention load trend and live event tape.
+ +
+
+ Decoder + {decoderLabel(activeDecoder)} +
+
+ PER + {perValue !== null ? `${perValue.toFixed(3)}%` : "N/A"} +
+
+ Session + + {activeIntegrationSession + ? `#${activeIntegrationSession.id.slice(0, 8).toUpperCase()} ${sessionStatusLabel( + activeIntegrationSession.status, + )}` + : "none"} + +
+
+ +
+
+ Intervention Load + {latestInterventionRoundLabel} +
+
+ Round Aggregate + + {opsInterventionSeries.length > 0 + ? `${opsInterventionSeries[opsInterventionSeries.length - 1]?.loadIndex.toFixed(1)}% load` + : "No load"} + +
+
+ + + + + + + { + const numeric = numericValue(value); + if (name === "Load Index") { + return [`${numeric.toFixed(1)}%`, name]; + } + return [numeric.toFixed(1), name]; + }} + contentStyle={{ background: "#0f0f0f", border: "1px solid #1f1f1f", borderRadius: 8 }} + labelStyle={{ color: "#c8d0db" }} + /> + + + + + +
+
+ +
+ Live Event Stream + {mode === "api" ? "Live API" : "Mock Feed"} +
+
+ {opsLiveEvents.map((event, index) => ( +
+ + {event.text} + {event.tag} +
+ ))} +
+
+ ); + + const rightRailHost = typeof document !== "undefined" ? document.getElementById("app-right-rail") : null; + const circuitDesignProviderFamily: CircuitProviderFamily = pendingCircuitLaunch + ? resolveProviderFamily(pendingCircuitLaunch.provider) + : launchProvider + ? resolveProviderFamily(launchProvider) + : "unknown"; + const liveConsoleRail = ( +
+
+
Live Console
+
Realtime decoder stream for operator context.
+ + {showLiveConsole ? ( + renderInterpretationPanel() + ) : ( +
Live console hidden.
+ )} +
+
+
Scientific Summary
+
+ Logical correction view with hardware-derived evidence and exact denominators. + ● {scientificExactnessLabel} + {sessionRunning && scientificState.state === "INGESTING" ? ( + + ● Session running — ingesting telemetry + + ) : null} +
+ + {scientificState.state === "VALIDATED" ? ( +
+ Scientific validation passed. Exact scientific contracts and validation checks are both + satisfied. +
+ ) : null} + {scientificState.state === "DEGRADED" ? : null} + {scientificState.state === "IDLE" ? ( + setSessionLauncherMenuOpen(true)} + startDisabled={Boolean(scientificSessionUnavailableReason) || launcherStatus === "launching"} + startDisabledReason={scientificSessionUnavailableReason} + launcherDisabled={launcherStatus === "launching"} + /> + ) : null} + {scientificState.state === "INGESTING" ? ( +
+ {scientificIngestingRows.map((row) => ( +
+ {row.label} + {row.value} +
+ ))} + {scientificIngestingRows.length === 0 ? ( +
+ Status + Computing logical metrics... +
+ ) : null} +
+ ) : null} +
+
+ ); + + return ( + <> +
+
+
+

LiDMaS+ Decoder

+

Scientific decoder instrument for logical correction

+
+
● {healthBadgeLabel}
+
+ +
+
+ Hardware / Provider + {heroHardwareLabel} · {heroProviderLabel} +
+
+ Run / Session + Run {activeRunShortId} · Session {activeSessionShortId} +
+
+ Mode + Scientific +
+
+ Active Decoder + {decoderLabel(activeDecoder)} +
+
+ Data Source / Exactness + + {systemOff ? "Off" : !systemArmed ? "Standby" : mode === "api" ? "Live API" : "GKP Mock"} · {scientificExactnessLabel} + +
+
+ Last Refresh + {systemOff ? "off" : !systemArmed ? "standby" : formatAgo(dataUpdatedAt)} +
+
+ Exactness Notes + {scientificMissingSignalsLabel} +
+
+ +
+
+ + +
+
+ + +
+
+ + setNeuralModelPath(event.target.value)} + placeholder="/absolute/path/to/model.onnx" + /> +
+
+ +
+ setSessionLauncherMenuOpen((current) => !current)} + onStartScientific={handleStartScientificSession} + onOpenBenchmark={handleOpenBenchmarkDialog} + onOpenReplay={handleOpenReplayDialog} + onStopSession={handleStopSession} + onViewRun={handleViewRun} + canViewRun={Boolean(activeRunId)} + scientificDisabledReason={scientificSessionUnavailableReason} + benchmarkDisabledReason={benchmarkMenuDisabledReason} + replayDisabledReason={replaySessionUnavailableReason} + showIngestingChip={sessionRunning && scientificState.state === "INGESTING"} + /> + + + + +
+
+
+ +
+ {DECODERS.map((decoder) => ( + + ))} +
+ +
+ Scope: {providerCount} providers, {jobsCount} jobs, {runsCount} runs. Active provider: {activeProviderName}. + {" "}Launch provider state: {providerOperationalStateText}. + {showingHistoricalEvidenceRun ? ( + + {" "} + Showing latest run with scientific evidence (Run{" "} + {latestScopedRunWithEvidence?.id.slice(0, 8).toUpperCase()}) while the newest run has no telemetry yet. + + ) : null} + {usingWarmupTelemetry ? ( + + {" "} + Session is warming up. Showing provisional telemetry until the first exact replay frames arrive. + + ) : null} + {activeDecoder === "neural_mwpm" ? ( + + {" "} + {neuralModelPath.trim() + ? "Neural model path configured." + : "Set Neural Model path before running neural_mwpm in replay mode."} + + ) : null} + {quickLaunchMessage ? ( + + {" "} + {quickLaunchMessage} + + ) : null} +
+ {activeRunId ? ( +
+ + + + +
+ ) : null} +
+ +
+
Scientific Summary
+
Exact scientific decoder metrics for the selected run and decoder.
+
+
+ {scientificPrimaryCards.map(({ key, contract }) => ( + + ))} +
+ +
+
+
Scientific Detail
+
Secondary exact counts and state-aware availability reasons.
+
+ {scientificSecondaryCards.map(({ key, contract, availability }) => ( +
+ {contract.label} + + {availability.available || scientificZeroBaseline + ? scientificCardValues[key] + : `${contract.label} unavailable — ${availability.availabilityReason}`} + +
+ ))} +
+ Decoder policy readout + {decoderLabel(activeDecoder)} selected in scientific mode +
+
+
+
+
+ + {scientificZeroBaseline ? ( +
+ Scientific summary initialized. Values remain zero until a job/run starts and telemetry is ingested. +
+ ) : null} + + {scientificState.state === "PARTIAL" && scientificMissingPrimaryReasons.length > 0 ? ( +
+ {scientificMissingPrimaryReasons.map((reason) => ( +
{reason}
+ ))} +
+ ) : null} + +
Operational Diagnostics
+
Runtime and synthetic indicators separated from scientific metrics.
+
+ {operationalKpiCards.map((card) => { + const trendPositive = card.trendUpGood ? card.trendDelta >= 0 : card.trendDelta <= 0; + return ( +
+
{card.label}
+
{card.value}
+
{card.trendText}
+
+ ); + })} +
+ +
+
+
Platform Health
+
Backend status
+
+ Status + + {systemOff + ? "Off" + : isApi && healthQuery.isError + ? "API unreachable" + : isApi && healthQuery.isLoading + ? "Loading" + : healthData?.status ?? "Unavailable"} + +
+
+ Uptime + + {systemOff ? "0s" : healthData ? `${uptimeSeconds.toLocaleString()}s` : "—"} + +
+
+ Latency + + {systemOff + ? "0ms" + : healthProbeLatencyMs !== null + ? `${healthProbeLatencyMs}ms` + : isApi && healthQuery.isLoading + ? "Probing..." + : "—"} + +
+
+ Version + + {systemOff ? "—" : healthData?.version ?? (isApi ? "—" : "v0.1.0")} + +
+
+ +
+
Workspace
+
Active entities
+
+ Providers + {providerCount} / 12 +
+
+ Jobs Queue + {queuedJobsCount} +
+
+ Runs Active + {activeRunsCount} +
+
+ Scope Payload + {formatBytes(scopePayloadBytes)} +
+
+ Hardware Mix + {hardwareMix.length > 0 ? hardwareMix[0].label : "Unknown"} +
+
+ +
+
+
+
Activity Feed
+
Recent events and runtime mode
+
+ + ● {mode === "api" ? "Live API" : "GKP Mock"} + +
+ + {activityFeed.map((item, index) => ( +
+ + {item.text} + {item.time} +
+ ))} +
+
+
+ +
+
QEC Encoding State Map
+
+ + +
+ {encodingMapMode === "surface" ? ( +
+ Outer code +
+ {[3, 5, 7].map((distance) => ( + + ))} +
+
+ ) : null} +
+
+
+
+
{qecMapHeading}
+
{qecMapSubtitle}
+
+ + ● {extractionStatusLabel} + +
+ + {encodingMapMode === "surface" && latestRoundSyndromes.length > 0 ? ( +
+ + {qecLattice.edges.map((edge) => ( + + ))} + {qecPointerNodes.map((node) => { + const bubbleX = Math.min(548, node.x + 16); + const bubbleY = Math.max(16, node.y - 22); + return ( + + + + + {node.label} + + + ); + })} + {qecLattice.nodes.map((node) => ( + + + + + {node.label} + + + ))} + +
+ ) : null} + + {encodingMapMode === "surface" && latestRoundSyndromes.length === 0 ? ( +
+ {telemetryInitializing + ? "Scientific session started. Waiting for first syndrome telemetry batch from replay." + : "No syndrome extraction stream found for the active run. Start a run with telemetry to render the lattice map."} +
+ ) : null} + + {encodingMapMode === "gkp" && gkpOscillatorMapPoints.length > 0 ? ( +
+ + + + + + +q + + + +p + + + -q + + + -p + + + {gkpPointerPoints.map((point) => { + const bubbleX = Math.min(542, point.x + 14); + const bubbleY = Math.max(18, point.y - 18); + return ( + + + + + {point.mode} + + + ); + })} + + {gkpOscillatorMapPoints.map((point) => ( + + + + ))} + +
+ ) : null} + + {encodingMapMode === "gkp" && gkpOscillatorMapPoints.length === 0 ? ( +
+ No raw GKP oscillator state telemetry was found. Add `gkp_oscillator_states` in run telemetry to + visualize direct phase-space states. +
+ ) : null} +
+ + +
+
+ +
+
Physical Noise & Realtime Monitor
+
+
+
+
Physical Channel Noise (Pre-QEC)
+ +
+ + {isPhysicalPanelCollapsed ? ( +
+ PER + {perValue !== null ? `${perValue.toFixed(3)}%` : "N/A"} +
+ ) : ( + <> + {physicalNoiseLoading ? ( +
Loading physical noise telemetry from backend...
+ ) : null} + {telemetryUnavailableForRun || telemetryInitializing ? ( +
+ {telemetryInitializing + ? "Scientific session started. Waiting for first physical-noise batch from replay." + : "No physical telemetry has been ingested for this run yet. Start or resume a scientific session to stream pre-QEC physical noise."} +
+ ) : null} + {physicalNoiseError ? ( +
Failed to load physical noise telemetry from backend.
+ ) : null} + {!physicalNoiseLoading && !physicalNoiseError && physicalNoiseData.length > 0 ? ( +
+
+ Latest sample: QEC round {latestPhysicalPoint ? latestPhysicalPoint.round : "N/A"} + = anomalyThresholds.perWarn ? "is-breach" : "is-ok" + }`} + > + PER warning threshold {anomalyThresholds.perWarn.toFixed(1)}% + +
+
+ {PHYSICAL_LEGEND_SIGNALS.map((signal) => ( + + + + + {signal.label} + {signal.format(latestPhysicalPoint)} + + ))} +
+ + + + + + + { + const numeric = numericValue(value); + if (name === "Displacement Sigma") { + return [numeric.toFixed(4), "Displacement Sigma"]; + } + return [`${numeric.toFixed(3)}%`, name]; + }} + contentStyle={{ background: "#0f0f0f", border: "1px solid #1f1f1f", borderRadius: 8 }} + labelStyle={{ color: "#c8d0db" }} + /> + + + + + + {physicalAnomalies.slice(0, 14).map((point) => ( + + ))} + + +
+ ) : null} + {!physicalNoiseLoading && + !physicalNoiseError && + !telemetryUnavailableForRun && + !telemetryInitializing && + physicalNoiseData.length === 0 ? ( +
+ Physical noise is captured before QEC and can be visualized directly when telemetry exists. +
+ ) : null} + + )} +
+ +
+
+
Real-Time Decoder Monitor
+
+
+ + +
+
+ + +
+
+
+ +
+ {DECODERS.map((decoder) => ( + + ))} +
+ +
+ + + + +
+ +
+ {chartLabel(activeChart)} · {decoderLabel(activeDecoder)} +
+ {activeDecoderMissingTelemetry ? ( +
Selected decoder has no intervention stream in this run.
+ ) : null} + {compareDecoderMissingTelemetry ? ( +
Compare decoder has no intervention stream in this run.
+ ) : null} + {telemetryUnavailableForRun || telemetryInitializing ? ( +
+ {telemetryInitializing + ? `Session ${activeSessionShortId} is running. Waiting for first replay telemetry batch for run ${activeRunShortId}.` + : `No scientific telemetry is attached to run ${activeRunShortId} yet. Start a session or choose a run with telemetry.`} +
+ ) : null} + {runTelemetryHardError ? ( +
Failed to load realtime monitor telemetry from backend.
+ ) : null} + {compareMode ? ( +
+ Compare vs {decoderLabel(compareDecoder)}: {formatTrend(compareDelta)} +
+ ) : null} +
+ {activeChart === "noise" && monitoringHasRows ? ( + + + + + + + + + + + `${(value * 100).toFixed(1)}%`} + label={{ value: "Noise Rate (%)", angle: -90, position: "insideLeft", fill: "#8f9eb4", fontSize: 10 }} + /> + [`${(numericValue(value) * 100).toFixed(2)}%`, "Noise"]} + contentStyle={{ background: "#0f0f0f", border: "1px solid #1f1f1f", borderRadius: 8 }} + labelStyle={{ color: "#c8d0db" }} + /> + + + + + {compareMode ? ( + + ) : null} + {noiseAnomalies.slice(0, 20).map((point) => ( + + ))} + + + ) : null} + + {activeChart === "success" && monitoringHasRows ? ( + + + + + + [`${numericValue(value).toFixed(2)}%`, "Success"]} + contentStyle={{ background: "#0f0f0f", border: "1px solid #1f1f1f", borderRadius: 8 }} + labelStyle={{ color: "#c8d0db" }} + /> + + + + {compareMode ? ( + + ) : null} + {successAnomalies.slice(0, 20).map((point) => ( + + ))} + + + ) : null} + + {activeChart === "error" && monitoringHasRows ? ( + + + + + + [`${numericValue(value).toFixed(2)}%`, "Error"]} + contentStyle={{ background: "#0f0f0f", border: "1px solid #1f1f1f", borderRadius: 8 }} + labelStyle={{ color: "#c8d0db" }} + /> + + + + {compareMode ? ( + + ) : null} + {errorAnomalies.slice(0, 20).map((point) => ( + + ))} + + + ) : null} + + {activeChart === "latency" && monitoringHasRows ? ( + + + + + + + + + + + + [`${numericValue(value).toFixed(1)} ms`, "Latency"]} + contentStyle={{ background: "#0f0f0f", border: "1px solid #1f1f1f", borderRadius: 8 }} + labelStyle={{ color: "#c8d0db" }} + /> + + + + {compareMode ? ( + + ) : null} + {latencyAnomalies.slice(0, 20).map((point) => ( + + ))} + + + ) : null} + {!monitoringHasRows ? ( +
+ {telemetryInitializing + ? "Realtime monitor is initializing from replay stream..." + : telemetryUnavailableForRun + ? `No realtime decoder telemetry is available for run ${activeRunShortId} yet.` + : runTelemetryHardError + ? "Realtime monitor data is temporarily unavailable due to a backend error." + : "Realtime monitor will populate when decoder telemetry is ingested."} +
+ ) : null} +
+
+
+
+ +
+
Operational Workflow
+
+ Non-scientific remediation workflow for operational anomalies. +
+ + {showOperationalWorkflow ? ( +
+ {workflowAlerts.map((alert) => { + const state = alertWorkflow[alert.id] ?? { + acknowledged: false, + owner: "Unassigned", + notes: "", + }; + return ( +
+
+
+
{alert.title}
+
{alert.detail}
+
+ + {alert.level.toUpperCase()} + +
+
{alert.metric}
+
+ + +
+