diff --git a/.github/workflows/desktop-macos.yml b/.github/workflows/desktop-macos.yml
new file mode 100644
index 000000000..ed59dfce9
--- /dev/null
+++ b/.github/workflows/desktop-macos.yml
@@ -0,0 +1,58 @@
+name: Desktop (macOS)
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [main]
+ paths:
+ - 'apps/desktop/**'
+ - 'apps/web/**'
+ - 'packages/**'
+ - 'scripts/desktop-stage.mjs'
+ - 'scripts/desktop-publish-release.mjs'
+ - 'src/**'
+
+permissions:
+ contents: write
+
+jobs:
+ build-macos:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: npm
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@1.88.0
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Stage desktop runtime
+ run: npm run stage:desktop
+
+ - name: Build desktop app
+ run: npm run build:app -w @codedelta/desktop -- --bundles dmg
+
+ - name: Upload dmg artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: codedelta-macos-dmg
+ path: |
+ apps/desktop/src-tauri/target/release/bundle/dmg/*.dmg
+ if-no-files-found: error
+
+ - name: Publish dmg to GitHub Release
+ if: github.ref == 'refs/heads/main'
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ set -euo pipefail
+ DMG=$(ls apps/desktop/src-tauri/target/release/bundle/dmg/*.dmg)
+ node scripts/desktop-publish-release.mjs macos "$DMG"
+
+ # Optional: add APPLE_CERTIFICATE / notarytool steps when signing secrets are configured.
diff --git a/.github/workflows/desktop-windows.yml b/.github/workflows/desktop-windows.yml
new file mode 100644
index 000000000..06f6c2866
--- /dev/null
+++ b/.github/workflows/desktop-windows.yml
@@ -0,0 +1,60 @@
+name: Desktop (Windows)
+
+on:
+ workflow_dispatch:
+ push:
+ branches: [main]
+ paths:
+ - 'apps/desktop/**'
+ - 'apps/web/**'
+ - 'packages/**'
+ - 'scripts/desktop-stage.mjs'
+ - 'scripts/desktop-publish-release.mjs'
+ - 'src/**'
+
+permissions:
+ contents: write
+
+jobs:
+ build-windows:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: npm
+
+ - name: Install Rust
+ uses: dtolnay/rust-toolchain@1.88.0
+
+ - name: Install NSIS
+ run: choco install nsis -y
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Stage desktop runtime
+ run: npm run stage:desktop
+
+ - name: Build desktop app
+ run: npm run build:app -w @codedelta/desktop -- --bundles nsis
+
+ - name: Upload Windows installer artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: codedelta-windows-exe
+ path: |
+ apps/desktop/src-tauri/target/release/bundle/nsis/*.exe
+ if-no-files-found: error
+
+ - name: Publish installer to GitHub Release
+ if: github.ref == 'refs/heads/main'
+ shell: bash
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ set -euo pipefail
+ EXE=$(ls apps/desktop/src-tauri/target/release/bundle/nsis/*.exe)
+ node scripts/desktop-publish-release.mjs windows "$EXE"
diff --git a/.gitignore b/.gitignore
index 7c949e1c2..31a61198e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,18 @@ npm-debug.log*
# CodeGraph data directories (in test projects)
.codegraph/
+# CodeDelta local cache (repos, snapshots, settings)
+.codedelta/
+.codedelta-desktop-smoke/
+
+# Desktop bundled runtime (generated by npm run stage:desktop)
+apps/desktop/src-tauri/resources/runtime/
+apps/desktop/src-tauri/target/
+apps/desktop/src-tauri/.cargo-home/
+
+# Local-only docs (not for GitHub)
+docs/codedelta/wechat-article-codedelta.md
+
test_frameworks
# Test language repos for manual testing
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bc5086a1..60f73c839 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
+- **CodeDelta panorama view (全景图).** Interactive call-flow graph built from CodeGraph snapshots: entry exploration from routes/components/exported symbols, Delta View graph tab with change coloring, Trace View navigation with evidence highlighting, optional LLM node labels, and Android MAIN/LAUNCHER activity entry detection in CodeGraph.
- **Java / Kotlin imports now resolve by fully-qualified name.** Extraction
wraps every top-level declaration of a `.kt` / `.java` file in a `namespace`
node carrying the file's `package` (so a class `Bar` in
diff --git a/LICENSE b/LICENSE
index 31c84c9c8..46f35fda3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
MIT License
-Copyright (c) 2026 Colby Mchenry
+Copyright (c) 2026 Colby Mchenry (CodeGraph — src/, CLI, MCP)
+Copyright (c) 2026 ingeniousfrog and CodeDelta contributors (packages/, apps/web/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 1db026097..4fd46acc0 100644
--- a/README.md
+++ b/README.md
@@ -1,595 +1,472 @@
-# CodeGraph
+
-### Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, and Kiro with Semantic Code Intelligence
+# CodeDelta
-**~35% cheaper · ~70% fewer tool calls · 100% local**
+**Local-first, commit-aware structural code intelligence**
-### [Documentation & Website →](https://colbymchenry.github.io/codegraph/)
+Built on [CodeGraph](https://github.com/colbymchenry/codegraph) · commit-level diff, trace, panorama & wiki
-[](https://www.npmjs.com/package/@colbymchenry/codegraph)
-[](https://opensource.org/licenses/MIT)
-[](https://nodejs.org/)
+
-[](#supported-platforms)
-[](#supported-platforms)
-[](#supported-platforms)
+[](https://github.com/ingeniousfrog/CodeDelta/actions/workflows/desktop-macos.yml)
+[](https://github.com/ingeniousfrog/CodeDelta/actions/workflows/desktop-windows.yml)
+[](https://github.com/ingeniousfrog/CodeDelta/releases)
+
+
-[](#supported-agents)
-[](#supported-agents)
-[](#supported-agents)
-[](#supported-agents)
-[](#supported-agents)
-[](#supported-agents)
-[](#supported-agents)
-[](#supported-agents)
+
-
+[⬇ Download](#desktop-macos--windows) · [⚡ Quick start](#quick-start) · [📖 简体中文](README.zh-CN.md)
-## Get Started
+
-**No Node.js required** — one command grabs the right build for your OS:
+**English** · [简体中文](README.zh-CN.md)
-```bash
-# macOS / Linux
-curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh
+
-# Windows (PowerShell)
-irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex
-```
+---
-Already have Node? Use npm instead (works on any version):
+CodeDelta answers **“how did this codebase change over time?”** by building a deterministic structural graph at each commit, then diffing, tracing, visualizing, and documenting from that graph — not from line diffs or text-chunk RAG alone.
-```bash
-npx @colbymchenry/codegraph # zero-install, or:
-npm i -g @colbymchenry/codegraph
-```
+| | Capability | What you get |
+|:--:|------------|--------------|
+| Δ | **[Delta View](#delta-view)** | Structural compare between two commits: symbols, edges, blast radius, impact score, file diff |
+| 🔍 | **[Trace View](#trace-view)** | Natural-language questions → ranked candidate commits with verifiable evidence |
+| ◎ | **[Panorama](#panorama)** | Interactive call-flow graph at one commit (or delta-colored overlay) |
+| 📄 | **[Wiki](#wiki-graph-grounded-docs--ask)** | Per-commit docs, Mermaid from real graph edges, optional LLM narration, **Ask this repo** |
-CodeGraph bundles its own runtime — nothing to compile, no native build, works the same everywhere. The interactive installer auto-configures your agent(s) — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro.
+This repository is a fork: the **CodeGraph** engine lives under [`src/`](src/) (CLI + MCP + tree-sitter graph). The **CodeDelta** app lives under [`packages/`](packages/) and [`apps/web/`](apps/web/) (import, timeline, delta, trace, panorama, wiki, settings UI).
-### Initialize Projects
+## Architecture
-```bash
-cd your-project
-codegraph init -i
-```
+### Product flow
-
+How the four views connect around a single imported repository and its commit history:
-
+```mermaid
+flowchart TB
+ Import["Import repo\n(GitHub URL or local path)"]
+ Timeline["Commit timeline\nbranch + history"]
-
+ Import --> Timeline
-### Uninstall
+ Timeline --> Delta["Delta View\nbase → head structural compare"]
+ Timeline --> Trace["Trace View\nwhich commit introduced this?"]
+ Timeline --> Panorama["Panorama\ncall-flow graph at one commit"]
+ Timeline --> Wiki["Wiki\ngenerate docs + Ask"]
-Changed your mind? One command removes CodeGraph from every agent it configured:
+ Trace -->|"verify candidate"| Delta
+ Delta -->|"Graph tab / drill-down"| Panorama
+ Wiki -->|"symbol citations"| Panorama
+ Wiki --> Ask["Ask this repo\nLLM + graph evidence whitelist"]
-```bash
-codegraph uninstall
+ Provider["Settings → Provider\n(Codex / OpenAI / none)"]
+ Provider -.->|"optional narration"| Trace
+ Provider -.->|"narration + Ask (required)"| Wiki
```
-Reverses the installer — strips CodeGraph's MCP server config, instructions, and permissions from each configured agent. Your project indexes (`.codegraph/`) are left untouched; remove those per-project with `codegraph uninit`. Use `--target` to remove from specific agents, or `--yes` to run non-interactively.
+### Project layout (technical)
+
+```mermaid
+flowchart TB
+ subgraph Clients["Clients"]
+ Web["apps/web\nReact UI"]
+ Desktop["apps/desktop\nTauri 2 + bundled Node 22"]
+ end
+
+ subgraph Server["packages/codedelta-server"]
+ API["REST API\nimport · compare · trace · wiki · panorama"]
+ end
+
+ subgraph Engines["Analysis engines"]
+ Snap["snapshot-manager\nworktree + cache"]
+ Diff["graph-diff · impact-score · delta-summary"]
+ Sub["graph-subgraph\nPanorama layout"]
+ Trace["trace-engine"]
+ WikiEng["wiki-engine\nTOC · pages · Mermaid · Ask retrieval"]
+ Prov["provider-runtime\noptional LLM"]
+ end
+
+ subgraph Core["src/ — CodeGraph"]
+ CG["tree-sitter extract → SQLite\nexportGraph per commit"]
+ end
+
+ subgraph Cache[".codedelta/ (local)"]
+ Repos["repos/ clones"]
+ Snaps["snapshots/ per commit"]
+ Wikis["wiki/ generated pages"]
+ end
+
+ Web --> API
+ Desktop --> API
+ API --> Snap
+ API --> Diff
+ API --> Sub
+ API --> Trace
+ API --> WikiEng
+ API --> Prov
+ Snap --> CG
+ Snap --> Repos
+ Diff --> Snaps
+ Sub --> Snaps
+ Trace --> Snaps
+ WikiEng --> Snaps
+ WikiEng --> Wikis
+ Trace --> Prov
+ WikiEng --> Prov
+```
----
+Wiki generation is **[DeepWiki](https://github.com/AsyncFuncAI/deepwiki-open)-inspired** but **graph-grounded**: TOC, diagrams, and citations come from CodeGraph snapshots; the LLM only adds narrated prose and Ask answers on top of a fixed evidence whitelist (no invented symbols or edges).
-## Why CodeGraph?
+## How CodeDelta differs from CodeGraph and Understand Anything
-When Claude Code explores a codebase, it spawns **Explore agents** that scan files with grep, glob, and Read — consuming tokens on every tool call.
+| | **CodeGraph** | **CodeDelta** | **Understand Anything** |
+|---|---------------|---------------|-------------------------|
+| **Primary question** | What is the structure *now*? Who calls whom? | How did structure *change* between two commits? Which commit likely introduced a shift? | What does this repo *mean*? How do I onboard onto it? |
+| **Unit of work** | Current workspace / indexed tree | `base commit → head commit` (+ commit-history trace) | Whole repo (or docs) snapshot |
+| **Output** | MCP tools, callers/callees, context for agents | Delta summary, impact score, file diff, trace candidates + evidence, **per-commit Wiki + Ask** | Interactive graph dashboard, tours, plain-English node summaries |
+| **Analysis** | Deterministic tree-sitter graph (SQLite) | Same graph **per commit snapshot**, then structural diff | Multi-agent pipeline + LLM-enriched graph (`.understand-anything/`) |
+| **AI role** | Optional (agent uses graph via MCP) | Optional for Trace (deterministic path always works) | Central to explanations and tours |
+| **Best for** | Day-to-day coding agents, refactors, “where is X?” | Release review, regressions, “when did this behavior start?” | Greenfield onboarding, architecture exploration |
-**CodeGraph gives those agents a pre-indexed knowledge graph** — symbol relationships, call graphs, and code structure. Agents query the graph instantly instead of scanning files.
+**Use together:** CodeGraph (or CodeDelta’s embedded engine) for **live** structure; CodeDelta when you care about **history and commit-level risk**; Understand Anything when you need a **guided map of the whole repo** for humans joining the project — not a substitute for commit-to-commit structural delta.
-### Benchmark Results
+## Features
-Tested across **7 real-world open-source codebases** spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question **with** and **without** CodeGraph. Each cell is the savings at the **median of 4 runs per arm**. _Re-validated on **v0.9.4** (2026-05-24)._
+### Delta View
-> **Average: 35% cheaper · 57% fewer tokens · 46% faster · 71% fewer tool calls**
+Compare two commits (`Base` = before, `Head` = after):
-| Codebase | Language | Cost | Tokens | Time | Tool calls |
-|----------|----------|------|--------|------|------------|
-| **VS Code** | TypeScript · ~10k files | 26% cheaper | 78% fewer | 52% faster | 85% fewer |
-| **Excalidraw** | TypeScript · ~640 | 52% cheaper | 90% fewer | 73% faster | 96% fewer |
-| **Django** | Python · ~3k | 12% cheaper | 36% fewer | 19% faster | 53% fewer |
-| **Tokio** | Rust · ~790 | 82% cheaper | 86% fewer | 71% faster | 92% fewer |
-| **OkHttp** | Java · ~645 | 2% cheaper | 13% fewer | 31% faster | 45% fewer |
-| **Gin** | Go · ~110 | 21% cheaper | 34% fewer | 27% faster | 40% fewer |
-| **Alamofire** | Swift · ~110 | 47% cheaper | 64% fewer | 48% faster | 83% fewer |
+- Changed symbols (functions, classes, components, routes)
+- Added/removed dependency edges (`calls`, `imports`)
+- Affected nodes from graph traversal
+- Deterministic impact score with severity and explanation
+- Delta summary (main areas, risks, suggested review order)
+- File-level unified diff modal (click files or symbols)
+- Per-snapshot metadata (`codegraph` vs `fallback` extraction)
+- **Graph tab** — React Flow call tree at the head commit with added/modified/removed coloring on nodes and edges
-The gains scale with codebase size: on large repos the agent answers from the index in a handful of calls with **zero file reads**, while the no-CodeGraph agent fans out across grep/find/Read (and the sub-agents it spawns). On a small repo like Gin (~150 files) native search is already cheap, so the margin narrows.
+### Panorama
-
-Full benchmark details
+Interactive call-flow graph from CodeGraph snapshots (React Flow):
-**Methodology.** Each arm is `claude -p` (Claude Opus 4.7) run headlessly against the repo with `--strict-mcp-config`: **WITH** = CodeGraph's MCP server enabled, **WITHOUT** = an empty MCP config. Built-in Read/Grep/Bash stay available to both. Same question per repo, **4 runs per arm, median reported**. Cost = the run's `total_cost_usd`; Tokens = total tokens processed (input incl. cached + output); Time = wall-clock; Tool calls = every tool invocation, including those inside any sub-agents the model spawns. Repos cloned at `--depth 1` and indexed by the same CodeGraph build that served them. Re-validated on codegraph **v0.9.4** (2026-05-24); per-repo numbers move run-to-run with how hard the without-arm thrashes (the median-of-4 smooths it, but tails remain — e.g. Tokio's without-arm hit $2.41/3m one batch).
+- **Single commit** — top entry routes/components/exported symbols, expandable by call depth
+- **Branch + commit** selectors; auto-rebuild when either changes
+- **Drill-down** — *Expand from here*, breadcrumb trail, Back / All entry points
+- **Shareable URLs** — `?branch=&commit=&depth=&focusPath=` preserves your drill-down path
+- **Export** — SVG (vector) or hi-DPI PNG generated from graph data (not a DOM screenshot)
+- Optional LLM node labels (non-authoritative)
+- Linked from **Delta View → Graph**, **Trace View**, and **Commit Timeline**
-**Queries:**
-| Codebase | Query |
-|----------|-------|
-| VS Code | "How does the extension host communicate with the main process?" |
-| Excalidraw | "How does Excalidraw render and update canvas elements?" |
-| Django | "How does Django's ORM build and execute a query from a QuerySet?" |
-| Tokio | "How does tokio schedule and run async tasks on its runtime?" |
-| OkHttp | "How does OkHttp process a request through its interceptor chain?" |
-| Gin | "How does gin route requests through its middleware chain?" |
-| Alamofire | "How does Alamofire build, send, and validate a request?" |
+### Trace View
-**Raw medians — WITH → WITHOUT:**
-| Codebase | Cost | Tokens | Time | Tool calls |
-|----------|------|--------|------|------------|
-| VS Code | $0.60 → $0.80 | 601k → 2.8M | 1m 10s → 2m 26s | 8 → 55 |
-| Excalidraw | $0.43 → $0.90 | 344k → 3.5M | 48s → 2m 58s | 3 → 79 |
-| Django | $0.59 → $0.67 | 739k → 1.2M | 1m 19s → 1m 38s | 9 → 19 |
-| Tokio | $0.42 → $2.41 | 379k → 2.6M | 53s → 3m 2s | 4 → 53 |
-| OkHttp | $0.47 → $0.47 | 636k → 730k | 42s → 1m 1s | 6 → 11 |
-| Gin | $0.37 → $0.47 | 444k → 675k | 44s → 1m 0s | 6 → 10 |
-| Alamofire | $0.61 → $1.14 | 1.0M → 2.8M | 1m 17s → 2m 27s | 12 → 69 |
+Describe a bug, behavior change, or question in natural language:
-**Why CodeGraph wins:** with the index available, the agent answers directly — `codegraph_context` to map the area, then one `codegraph_explore` for the relevant source — and stops, usually with zero file reads. Without it, the agent (and the Explore sub-agents it spawns) spends most of its budget on discovery (find/ls/grep) before reading the right code. CodeGraph only helps when queried *directly*, so its instructions steer agents to answer directly rather than delegate exploration to file-reading sub-agents — otherwise a sub-agent reads files regardless and CodeGraph becomes overhead.
+- Rank candidate commits from history (messages, paths, symbols, delta signals)
+- Attach evidence per candidate (`previous → candidate` compare when a parent exists)
+- Return a direct answer, confidence, uncertainty, and suggested next steps
+- Jump to Delta View to verify each candidate
+- **View in Panorama** on a candidate commit (trace symbol highlights when available)
-
+**Without any LLM configured**, Trace still returns candidates, evidence, and impact radius (evidence-first, no invented facts).
----
+### Wiki (graph-grounded docs + Ask)
-## Key Features
+Open **Wiki** from the commit timeline (or `/repos/:id/wiki`). Pick a commit → **Generate wiki** → browse the TOC, read pages, and use **Ask this repo** in the side panel.
-| | |
-|---|---|
-| **Smart Context Building** | One tool call returns entry points, related symbols, and code snippets — no expensive exploration agents |
-| **Full-Text Search** | Find code by name instantly across your entire codebase, powered by FTS5 |
-| **Impact Analysis** | Trace callers, callees, and the full impact radius of any symbol before making changes |
-| **Always Fresh** | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config |
-| **20+ Languages** | TypeScript, JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, C, C++, Objective-C, Swift, Kotlin, Dart, Lua, Luau, Svelte, Liquid, Pascal/Delphi |
-| **Framework-aware Routes** | Recognizes web-framework routing files and links URL patterns to their handlers across 14 frameworks |
-| **Mixed iOS / React Native / Expo** | Closes cross-language flows that static parsing misses: Swift ↔ ObjC bridging, React Native legacy bridge + TurboModules + Fabric view components, native → JS event emitters, Expo Modules |
-| **100% Local** | No data leaves your machine. No API keys. No external services. SQLite database only |
+Inspired by [DeepWiki](https://github.com/AsyncFuncAI/deepwiki-open), but built on **CodeGraph snapshots** instead of embedding/RAG over raw files:
-
-How auto-syncing works — and why you don't need to run codegraph sync manually
+| Layer | LLM required? | Content |
+|-------|---------------|---------|
+| **Structure** | No | TOC (overview, architecture, top modules, routes/components), symbol tables, file lists, README excerpts |
+| **Diagrams** | No | Mermaid module import graph and call flows — **serialized from real edges**, never invented |
+| **Narration** | Optional | Per-page prose when a provider is configured (“with LLM narration”) |
+| **Ask** | **Yes** | Conversational Q&A; lexical + graph retrieval → evidence whitelist → LLM answer with citations |
-When your agent (Claude Code, Cursor, Codex, opencode) launches `codegraph serve --mcp`, three layers keep the index in step with your code — and make sure the agent never gets a silent wrong answer in the brief window between an edit and the next sync:
+- **Citations** link symbols to file/line ranges; symbol citations open **Panorama** at the right commit
+- **Ask** bootstraps entry points + README when your question does not match symbols directly
+- Cached under `.codedelta/wiki////`; generation is a background job with progress
+- Engine: [`packages/codedelta-wiki-engine/`](packages/codedelta-wiki-engine/)
-1. **File watcher with debounced auto-sync.** A native FSEvents / inotify / ReadDirectoryChangesW watcher captures every source-file create / modify / delete and triggers a re-index after a debounce window (default `2000ms`, tunable via `CODEGRAPH_WATCH_DEBOUNCE_MS`, clamped to `[100ms, 60s]`). Bursts of edits collapse into a single sync.
+### Commit timeline & import
-2. **Per-file staleness banner.** During the brief debounce window, MCP tool responses that would reference a still-pending file prepend a `⚠️` banner naming it and telling the agent to `Read` it directly. Pending files NOT referenced by the response surface as a small footer instead. Either way, the agent gets an explicit signal — validated with Claude Code, where the agent literally says "Reading the file directly for the live content" before opening it.
+- Import a public GitHub repo (`owner/repo` or URL) or a **local git path**
+- Browse commits; open Delta, Trace, Panorama, or Wiki from the timeline
-3. **Connect-time catch-up.** When the MCP server (re)connects, codegraph runs a fast `(size, mtime)` + content-hash reconciliation against the working tree before answering the first query — so edits made while no MCP server was running (a `git pull` from the terminal, edits from another editor, a previous agent session that exited) get absorbed on the next session's first tool call.
+## What CodeDelta is not
-```
-agent writes src/Widget.ts
- → watcher fires (<100ms)
- → debounce (default 2s)
- → sync; Widget.ts is in the index
- → next agent query sees it
-```
+- **Not a generic Git GUI** — no merge UI or branch workflow
+- **Not a line-diff-first tool** — structural delta is the product; text diff supports review
+- **Not a replacement for Understand Anything** — no interactive whole-repo onboarding graph or LLM tours
-**Verify any time** with `codegraph_status` (via MCP) or `codegraph status` (CLI). If anything is pending, you'll see a `### Pending sync:` section naming the files and their edit age.
+## Quick start
-The handful of cases where manual `codegraph sync` makes sense: the watcher is disabled (sandboxed environments, or `CODEGRAPH_NO_DAEMON=1`), or you're scripting against the index outside an agent session and want a pre-flight sync at the start of your script.
+Requires **Node.js 20–24** and **git**.
-→ Full deep-dive in [Guides → Indexing a Project](https://colbymchenry.github.io/codegraph/guides/indexing/#stay-fresh-automatically).
+```bash
+git clone https://github.com/ingeniousfrog/CodeDelta.git
+cd CodeDelta
+npm install
+npm run build:codedelta
+npm run dev:codedelta
+```
-
+Open [http://localhost:3847](http://localhost:3847) (dev mode proxies the Vite UI to the API port; [http://localhost:5173](http://localhost:5173) also works).
----
+1. **Import** a repository (GitHub URL or local path)
+2. **Commit Timeline** — pick a branch and browse history
+3. **Delta View** — choose `Base (before)` and `Head (after)`, then compare
+4. **Trace View** — describe an issue; review candidates and open Delta to verify
+5. **Panorama** — pick branch/commit and explore call trees; drill down from any entry or route
+6. **Wiki** — pick a commit, generate the wiki, browse pages, and ask questions with cited answers
-## Framework-aware Routes
-
-CodeGraph detects web-framework routing files and emits `route` nodes linked by `references` edges to their handler classes or functions. Querying callers of a view/controller now surfaces the URL pattern that binds it.
-
-| Framework | Shapes recognized |
-|---|---|
-| **Django** | `path()`, `re_path()`, `url()`, `include()` in `urls.py` (CBV `.as_view()`, dotted paths) |
-| **Flask** | `@app.route('/path', methods=[...])`, blueprint routes |
-| **FastAPI** | `@app.get(...)`, `@router.post(...)`, all standard methods |
-| **Express** | `app.get(...)`, `router.post(...)` with middleware chains |
-| **NestJS** | `@Controller` + `@Get/@Post/...`, GraphQL `@Resolver` + `@Query/@Mutation`, `@MessagePattern`/`@EventPattern`, `@SubscribeMessage` |
-| **Laravel** | `Route::get()`, `Route::resource()`, `Controller@action`, tuple syntax |
-| **Drupal** | `*.routing.yml` routes (`_controller`, `_form`, entity handlers); `hook_*` implementations in `.module`/`.theme`/`.install`/`.inc` |
-| **Rails** | `get '/x', to: 'users#index'`, hash-rocket `=>` syntax |
-| **Spring** | `@GetMapping`, `@PostMapping`, `@RequestMapping` on methods |
-| **Gin / chi / gorilla / mux** | `r.GET(...)`, `router.HandleFunc(...)` |
-| **Axum / actix / Rocket** | `.route("/x", get(handler))` |
-| **ASP.NET** | `[HttpGet("/x")]` attributes on action methods |
-| **Vapor** | `app.get("x", use: handler)` |
-| **React Router** / **SvelteKit** | Route component nodes |
+## UI walkthrough
----
+### 1) Import repository
-## Mixed iOS / React Native / Expo bridging
+
-Real iOS and React Native codebases live across multiple languages — a Swift caller invokes an Objective-C selector that's been auto-bridged, a JS file calls into a native module via the React Native bridge, a JSX component delegates to a native view manager. Static tree-sitter extraction stops at each language boundary. CodeGraph bridges them so `trace`, `callers`, `callees`, and `impact` connect end-to-end across the gap.
+### 2) Commit timeline
-| Boundary | JS / Swift side | Native side | How |
-|---|---|---|---|
-| **Swift → ObjC** | Swift `obj.foo(bar:)` | ObjC selector `-fooWithBar:` | `@objc` auto-bridging rules (including init/property/protocol forms) + Cocoa preposition prefixes (`With`/`For`/`By`/`In`/`On`/`At`/…) |
-| **ObjC → Swift** | ObjC `[obj fooWithBar:]` | Swift `@objc func foo(bar:)` | Reverse-bridge name candidates; verifies `@objc` exposure from source |
-| **React Native legacy bridge** | JS `NativeModules.X.fn(...)` | ObjC `RCT_EXPORT_METHOD` / `RCT_REMAP_METHOD` · Java/Kotlin `@ReactMethod` | Parses macro/annotation declarations to build a JS-name → native-method map |
-| **React Native TurboModules** | JS `import M from './NativeM'; M.fn(...)` | Native impl matching the Codegen spec | Treats the `Native.ts` spec interface as ground truth |
-| **RN native → JS events** | JS `new NativeEventEmitter(...).addListener('e', cb)` | ObjC `[self sendEventWithName:@"e" body:...]` · Swift `sendEvent(withName: "e", ...)` · Java/Kotlin `.emit("e", ...)` | Synthesized cross-language event channel keyed by literal event name |
-| **Expo Modules** | JS `requireNativeModule('X').fn(...)` | Swift / Kotlin `Module { Name("X"); AsyncFunction("fn") { ... } }` | Parses the Expo DSL literals; synthetic method nodes resolve via existing name-match |
-| **Fabric view components** | JSX `` | TS Codegen spec + native impl class | Spec → `component` node; convention-based name+suffix lookup (`View`/`ComponentView`/`Manager`/`ViewManager`) bridges to native |
-| **Legacy Paper view managers** | JSX `` | ObjC `RCT_EXPORT_VIEW_PROPERTY` · Java/Kotlin `@ReactProp` | Same as Fabric — Paper-era declarations also produce `component` + `property` nodes |
+
-**Validated on real codebases** (small + medium + large for each bridge):
+### 3) Delta View (structural compare)
-| Bridge | Small | Medium | Large |
-|---|---|---|---|
-| Swift ↔ ObjC | [Charts](https://github.com/danielgindi/Charts) | [realm-swift](https://github.com/realm/realm-swift) | [Wikipedia-iOS](https://github.com/wikimedia/wikipedia-ios) |
-| RN legacy bridge | [AsyncStorage](https://github.com/react-native-async-storage/async-storage) | [react-native-svg](https://github.com/software-mansion/react-native-svg) | [react-native-firebase](https://github.com/invertase/react-native-firebase) |
-| RN native → JS events | [RNGeolocation](https://github.com/Agontuk/react-native-geolocation-service) | — | react-native-firebase |
-| Expo Modules | expo-haptics | expo-camera | expo SDK sweep (7 packages) |
-| Fabric / Paper views | [react-native-segmented-control](https://github.com/react-native-segmented-control/segmented-control) | [react-native-screens](https://github.com/software-mansion/react-native-screens) | [react-native-skia](https://github.com/Shopify/react-native-skia) |
+
-Each bridge emits edges tagged `provenance:'heuristic'` with `metadata.synthesizedBy:` set to a stable channel name (e.g. `swift-objc-bridge`, `rn-event-channel`, `fabric-native-impl`, `expo-module-extract`), so the agent can tell at a glance how a hop got into the graph.
+### 4) Trace View (evidence-first origin analysis)
----
+
-## Quick Start
+File-level modal details are also captured in:
+[`docs/images/delta-file-diff-modal.png`](docs/images/delta-file-diff-modal.png)
-### 1. Run the Installer
+API: [http://localhost:3847](http://localhost:3847)
-```bash
-npx @colbymchenry/codegraph
-```
+| Endpoint | Description |
+|----------|-------------|
+| `GET /api/health` | Health check |
+| `GET /api/repos/:id/compare?base=&head=` | Structural delta between commits |
+| `GET /api/repos/:id/panorama?commit=&depth=&root=` | Call-flow graph at one commit |
+| `GET /api/repos/:id/panorama?base=&head=&depth=` | Delta-colored call-flow graph (head commit tree) |
+| `POST /api/repos/:id/panorama/enrich` | Optional LLM labels for panorama nodes |
+| `GET /api/repos/:id/diff?base=&head=&file=` | Unified diff for one file |
+| `POST /api/repos/:id/trace` | Trace question → candidates + evidence |
+| `POST /api/repos/:id/wiki/generate?commit=` | Start wiki generation (background job) |
+| `GET /api/repos/:id/wiki/status?commit=` | Generation state + progress |
+| `GET /api/repos/:id/wiki/toc?commit=` | Wiki table of contents |
+| `GET /api/repos/:id/wiki/page?commit=§ion=` | One wiki page (markdown + citations) |
+| `GET /api/repos/:id/wiki/asset?commit=&path=` | README / wiki image at commit (git show) |
+| `POST /api/repos/:id/wiki/ask` | Question → cited answer over the commit graph |
+| `GET /api/settings/provider` | Current LLM provider settings |
+| `GET /api/settings/provider/codex-status` | Local Codex CLI login status |
-The installer will:
-- Ask which agent(s) to configure — auto-detects installed ones from: **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, **Kiro**
-- Prompt to install `codegraph` on your PATH (so agents can launch the MCP server)
-- Ask whether configs apply to all your projects or just this one
-- Write each chosen agent's MCP server config + an instructions file (e.g. `CLAUDE.md`, `.cursor/rules/codegraph.mdc`, `~/.codex/AGENTS.md`, `~/.gemini/GEMINI.md`)
-- Set up auto-allow permissions when Claude Code is one of the targets
-- Initialize your current project (local installs only)
+## Configure Codex for Trace View & Wiki (optional)
-**Non-interactive (scripting / CI):**
+CodeDelta can reuse your **existing Codex CLI login** — no API key pasted into the web UI.
+
+### 1. Log in with Codex CLI
```bash
-codegraph install --yes # auto-detect agents, install global
-codegraph install --target=cursor,claude --yes # explicit target list
-codegraph install --target=auto --location=local # detected agents, project-local
-codegraph install --print-config codex # print snippet, no file writes
+# Install Codex CLI if needed: https://github.com/openai/codex
+codex login
```
-| Flag | Values | Default |
-|---|---|---|
-| `--target` | `auto`, `all`, `none`, or csv (`claude,cursor,...`) | prompt |
-| `--location` | `global`, `local` | prompt |
-| `--yes` | (boolean) | prompt every step |
-| `--no-permissions` | (boolean) skip Claude auto-allow list | permissions on |
-| `--print-config ` | dump snippet for one agent and exit | — |
+This creates or updates `~/.codex/auth.json` (ChatGPT OAuth). You can override the directory with `CODEX_HOME`.
-### 2. Restart Your Agent
+### 2. Select Codex in CodeDelta
-Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro) for the MCP server to load.
+1. Open the app → **Settings → Provider Settings**
+2. Choose **Codex OAuth**
+3. Confirm the page shows that local login was detected (path under `~/.codex/`)
+4. **Model** — leave empty to use `model` from `~/.codex/config.toml`, or override (e.g. `gpt-5.5`)
+5. **Save settings**
-### 3. Initialize Projects
+### 3. Run Trace
-```bash
-cd your-project
-codegraph init -i
-```
+Open **Trace View**, enter a concrete question (file paths, symbols, or config names help), and click **Run trace**.
-Builds the per-project knowledge graph index. Also wires up any project-local agent surfaces (e.g. Cursor's `.cursor/rules/codegraph.mdc`) so a single global `codegraph install` works in every project you open — no need to re-run the installer per project.
+Deterministic results always appear; if Codex is configured, the model may refine the narrative. Model output is **non-authoritative** — evidence and Delta verification are the source of truth. The same provider also powers **Wiki** page narration and **Ask** (Ask requires a provider; wiki pages still ship structurally without one).
-That's it — your agent will use CodeGraph tools automatically when a `.codegraph/` directory exists.
+### Codex troubleshooting
-
-Manual Setup (Alternative)
+| Symptom | What to try |
+|---------|-------------|
+| “auth.json not found” | Run `codex login` on the same machine as the CodeDelta server |
+| `HTTP 400` / unsupported parameter | Restart `npm run dev:codedelta` after pulling latest (Codex backend ≠ OpenAI API) |
+| `fetch failed` / timeout | Check network/VPN; retry; see error details for `ENOTFOUND` / `ETIMEDOUT` |
+| AI box red but candidates look fine | Expected fallback — structural trace still works; fix Codex and rerun |
+| Changes to provider code not applied | `dev:codedelta` rebuilds packages on start; restart the dev server |
-**Install globally:**
-```bash
-npm install -g @colbymchenry/codegraph
-```
+**Other providers:** **No AI** (default), **OpenAI API key**, or **OpenAI-compatible** base URL + key. Anthropic and Ollama are not implemented yet.
-**Add to `~/.claude.json`:**
-```json
-{
- "mcpServers": {
- "codegraph": {
- "type": "stdio",
- "command": "codegraph",
- "args": ["serve", "--mcp"]
- }
- }
-}
-```
+## Local cache (`.codedelta/`)
-**Add to `~/.claude/settings.json` (optional, for auto-allow):**
-```json
-{
- "permissions": {
- "allow": [
- "mcp__codegraph__codegraph_search",
- "mcp__codegraph__codegraph_context",
- "mcp__codegraph__codegraph_callers",
- "mcp__codegraph__codegraph_callees",
- "mcp__codegraph__codegraph_impact",
- "mcp__codegraph__codegraph_node",
- "mcp__codegraph__codegraph_status",
- "mcp__codegraph__codegraph_files"
- ]
- }
-}
-```
-
-
-
-
-Global Instructions Reference
+| Path | Purpose |
+|------|---------|
+| `.codedelta/repos//` | Cloned or referenced repositories |
+| `.codedelta/registry.json` | Import registry |
+| `.codedelta/snapshots////` | Per-commit structural snapshots |
+| `.codedelta/wiki////` | Generated wiki (toc, pages, meta) |
+| `.codedelta/settings.json` | Provider settings |
-The installer automatically adds these instructions to `~/.claude/CLAUDE.md`:
+Snapshots are built **lazily** on compare/trace — full history is not pre-indexed.
-```markdown
-## CodeGraph
+## Extraction
-CodeGraph builds a semantic knowledge graph of codebases for faster, smarter code exploration.
+**Primary:** CodeGraph (`index` + `exportGraph`) in an isolated git worktree per commit.
-### If `.codegraph/` exists in the project
+**Fallback:** Minimal TS/JS extractor if CodeGraph fails; snapshots record `extractionMethod: "fallback"` and warnings.
-**Answer directly with CodeGraph — don't delegate exploration to a file-reading sub-agent or a grep/read loop.** CodeGraph *is* the pre-built search index; re-deriving its answers with grep + Read repeats work it already did and costs more for the same result. For "how does X work?", architecture, trace, or where-is-X questions, answer in a handful of CodeGraph calls and stop — typically with **zero file reads**. The returned source is complete and authoritative: treat it as already read and do not re-open those files. Reach for raw Read/Grep only to confirm a specific detail CodeGraph didn't cover.
+## Built on CodeGraph
-**Tool selection by intent:**
+The [`src/`](src/) tree is the upstream CodeGraph project:
-| Tool | Use For |
-|------|---------|
-| `codegraph_context` | Map a task / feature / area first — composes search + node + callers + callees in one call |
-| `codegraph_trace` | "How does X reach Y" — the call path, each hop's body inline (follows dynamic-dispatch hops grep can't) |
-| `codegraph_explore` | Survey several related symbols' source in ONE budget-capped call |
-| `codegraph_search` | Find a symbol by name |
-| `codegraph_callers` / `codegraph_callees` | Walk call flow one hop at a time |
-| `codegraph_impact` | Check what's affected before editing |
-| `codegraph_node` | Get a single symbol's source / signature |
+- Tree-sitter → SQLite knowledge graph
+- CLI: `codegraph init`, `codegraph sync`, `codegraph serve --mcp`
+- MCP tools for agents (search, callers, callees, trace, impact)
-A direct CodeGraph answer is a handful of calls; a grep/read exploration is dozens.
+Initialize CodeGraph in a repo when you also want agent-time MCP (separate from the CodeDelta web app):
-### If `.codegraph/` does NOT exist
+```bash
+npm run build
+npx codegraph init -i
+```
-At the start of a session, ask the user if they'd like to initialize CodeGraph:
+## Project layout
-"I notice this project doesn't have CodeGraph initialized. Would you like me to run `codegraph init -i` to build a code knowledge graph?"
+```
+src/ # CodeGraph core (upstream-compatible)
+packages/
+ codedelta-types/
+ codedelta-repo-manager/
+ codedelta-server/ # REST API
+ codedelta-snapshot-manager/
+ codedelta-graph-diff/
+ codedelta-graph-subgraph/ # Panorama call-tree + layout
+ codedelta-impact-score/
+ codedelta-delta-summary/
+ codedelta-trace-engine/
+ codedelta-wiki-engine/ # Wiki TOC/pages/Mermaid + Ask retrieval
+ codedelta-provider-runtime/
+apps/web/ # React UI (Delta, Trace, Panorama, Wiki)
+apps/desktop/ # macOS desktop shell (Tauri 2)
```
-
+Roadmap and deferred work: [docs/codedelta/ROADMAP.md](docs/codedelta/ROADMAP.md).
----
+## Limitations
-## How It Works
+- TypeScript/JavaScript-first practical path today
+- Delta and trace: **commit-to-commit** only (no PR/branch/working-tree compare yet)
+- Codex: local CLI session only (no in-browser OAuth)
+- Panorama overview shows **top entry surfaces** only on large repos — drill down with *Expand from here*; sparse graphs often mean mount points (`USE /api/*`) need expansion to see router internals
+- Panorama export is a simplified card layout (no live *Expand* buttons); prefer **SVG** for zoom/clarity
+- Symbol click opens **file** diff, not symbol-to-hunk mapping
-```
-┌───────────────────────────────────────────────────────────────────┐
-│ Claude Code │
-│ │
-│ "How does a request reach the database?" │
-│ calls CodeGraph tools directly — no Explore sub-agent │
-│ │ │
-└─────────────────────────────────┬─────────────────────────────────┘
- │
- ▼
-┌───────────────────────────────────────────────────────────────────┐
-│ CodeGraph MCP Server │
-│ │
-│ context · trace · explore · callers · callees · impact │
-│ │ │
-│ ▼ │
-│ SQLite knowledge graph │
-│ symbols · edges · files · FTS5 full-text search │
-└───────────────────────────────────────────────────────────────────┘
-```
+## Desktop (macOS & Windows)
-1. **Extraction** — [tree-sitter](https://tree-sitter.github.io/) parses source code into ASTs. Language-specific queries extract nodes (functions, classes, methods) and edges (calls, imports, extends, implements).
+CodeDelta ships **desktop apps** ([`apps/desktop/`](apps/desktop/)) — Tauri 2 shells that bundle Node 22 (for CodeGraph’s `node:sqlite`) and the API server. End users do not need a separate Node install.
-2. **Storage** — Everything goes into a local SQLite database (`.codegraph/codegraph.db`) with FTS5 full-text search.
+**Version** is read from `apps/desktop/src-tauri/tauri.conf.json` (currently `0.2.1`). macOS and Windows installers publish to the same GitHub Release: [`codedelta-desktop-v0.2.1`](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1). Desktop bundles include **Delta, Trace, Panorama, and Wiki** (Ask requires a configured LLM provider). **v0.2.0 was withdrawn** — its macOS DMG used a manual packaging path and was ~40 MB larger with no functional benefit; use v0.2.1.
-3. **Resolution** — After extraction, references are resolved: function calls → definitions, imports → source files, class inheritance, and framework-specific patterns.
+### Download
-4. **Auto-Sync** — The MCP server watches your project using native OS file events. Changes are debounced (2-second quiet window), filtered to source files only, and incrementally synced. The graph stays fresh as you code — no configuration needed.
+| Platform | File | Notes |
+|----------|------|-------|
+| **macOS** (Apple Silicon) | [GitHub Releases](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1) → `CodeDelta_*_aarch64.dmg` | Unsigned; right-click → Open if blocked |
+| **Windows** (x64) | [GitHub Releases](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1) → `CodeDelta_*_x64-setup.exe` | NSIS installer |
+| macOS mirror | [百度网盘](https://pan.baidu.com/s/1FQxOgNHyvU1Y5EB34RpogQ?pwd=frog) · 提取码: `frog` | **Legacy v0.1.0 only** — no Wiki; use [GitHub Releases](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1) for current builds |
----
+**Install (macOS):** open the dmg → drag **CodeDelta** to Applications.
-## CLI Reference
+If macOS says **“is damaged and can’t be opened”** after downloading from GitHub/Safari, that is Gatekeeper quarantine on unsigned apps — the app is not corrupt. Fix:
```bash
-codegraph # Run interactive installer
-codegraph install # Run installer (explicit)
-codegraph uninstall # Remove CodeGraph from your agents (inverse of install)
-codegraph init [path] # Initialize in a project (--index to also index)
-codegraph uninit [path] # Remove CodeGraph from a project (--force to skip prompt)
-codegraph index [path] # Full index (--force to re-index, --quiet for less output)
-codegraph sync [path] # Incremental update
-codegraph status [path] # Show statistics
-codegraph query # Search symbols (--kind, --limit, --json)
-codegraph files [path] # Show file structure (--format, --filter, --max-depth, --json)
-codegraph context # Build context for AI (--format, --max-nodes)
-codegraph callers # Find what calls a function/method (--limit, --json)
-codegraph callees # Find what a function/method calls (--limit, --json)
-codegraph impact # Analyze what code is affected by changing a symbol (--depth, --json)
-codegraph affected [files...] # Find test files affected by changes (see below)
-codegraph serve --mcp # Start MCP server
+xattr -cr /Applications/CodeDelta.app
```
-### `codegraph affected`
+Then open again, or right-click the app → **Open** the first time. (Baidu Netdisk downloads often need the same `xattr` step.)
-Traces import dependencies transitively to find which test files are affected by changed source files.
+**Install (Windows):** run the setup `.exe` and follow the wizard.
-```bash
-codegraph affected src/utils.ts src/api.ts # Pass files as arguments
-git diff --name-only | codegraph affected --stdin # Pipe from git diff
-codegraph affected src/auth.ts --filter "e2e/*" # Custom test file pattern
-```
-
-| Option | Description | Default |
-|--------|-------------|---------|
-| `--stdin` | Read file list from stdin | `false` |
-| `-d, --depth ` | Max dependency traversal depth | `5` |
-| `-f, --filter ` | Custom glob to identify test files | auto-detect |
-| `-j, --json` | Output as JSON | `false` |
-| `-q, --quiet` | Output file paths only | `false` |
+Requires **git** on `PATH` on both platforms. **Do not run `npm run dev:desktop` while using the installed app** — both use port 3847; the dev server is API-only and causes a blank `Cannot GET /` page.
-**CI/hook example:**
+**Runtime data:** `~/Library/Application Support/CodeDelta` (macOS) · `%APPDATA%\CodeDelta` (Windows)
-```bash
-#!/usr/bin/env bash
-AFFECTED=$(git diff --name-only HEAD | codegraph affected --stdin --quiet)
-if [ -n "$AFFECTED" ]; then
- npx vitest run $AFFECTED
-fi
-```
+### Build from source
----
+**Requirements:** target OS (macOS or Windows), [Rust 1.88+](https://rustup.rs/), repo dev dependencies (`npm ci`). macOS also needs Xcode Command Line Tools; Windows needs [NSIS](https://nsis.sourceforge.io/) for the installer.
-## MCP Tools
+```bash
+# One-time: stage embedded Node + server runtime (~200MB under apps/desktop/src-tauri/resources/runtime/)
+npm run stage:desktop
-When running as an MCP server, CodeGraph exposes these tools to Claude Code:
+# Build .app + .dmg (unsigned; Gatekeeper may prompt on first open)
+npm run build:desktop
-| Tool | Purpose |
-|------|---------|
-| `codegraph_search` | Find symbols by name across the codebase |
-| `codegraph_context` | Build relevant code context for a task |
-| `codegraph_trace` | Trace the call path between two symbols ("how does X reach Y") in one call — each hop with its body inline, following dynamic-dispatch hops (callbacks, React re-render, interface→impl) that grep can't |
-| `codegraph_callers` | Find what calls a function |
-| `codegraph_callees` | Find what a function calls |
-| `codegraph_impact` | Analyze what code is affected by changing a symbol |
-| `codegraph_node` | Get details about a specific symbol (optionally with source code) |
-| `codegraph_explore` | Return source for several related symbols grouped by file, plus a relationship map, in one call |
-| `codegraph_files` | Get indexed file structure (faster than filesystem scanning) |
-| `codegraph_status` | Check index health and statistics |
+# Dev: API + Vite + Tauri window (uses localhost:5173 + :3847, not bundled runtime)
+npm run dev:desktop
+```
----
+Output:
+- macOS: `apps/desktop/src-tauri/target/release/bundle/dmg/CodeDelta_*_aarch64.dmg`
+- Windows: `apps/desktop/src-tauri/target/release/bundle/nsis/CodeDelta_*_x64-setup.exe`
-## Library Usage
+**`apps/desktop/` layout:**
-```typescript
-import CodeGraph from '@colbymchenry/codegraph';
+```
+apps/desktop/
+ package.json # tauri dev / build:app scripts
+ src-tauri/
+ tauri.conf.json # window, bundle, embedded resources
+ src/server.rs # spawn bundled Node API on launch
+ resources/runtime/ # generated by npm run stage:desktop (gitignored)
+```
-const cg = await CodeGraph.init('/path/to/project');
-// Or: const cg = await CodeGraph.open('/path/to/project');
+**Git:** the app shows a banner if `git` is missing.
-await cg.indexAll({
- onProgress: (p) => console.log(`${p.phase}: ${p.current}/${p.total}`)
-});
+| Variable | Desktop default | Meaning |
+|----------|-----------------|---------|
+| `CODEDELTA_CACHE_DIR` | `~/Library/Application Support/CodeDelta` | Cache root (set by Tauri) |
+| `CODEDELTA_MONOREPO_ROOT` | bundled `runtime/app` | CodeGraph dist root |
+| `CODEDELTA_STATIC_DIR` | bundled `runtime/web-dist` | Web UI static files |
+| `CODEDELTA_DESKTOP` | `1` | Desktop mode flag |
-const results = cg.searchNodes('UserService');
-const callers = cg.getCallers(results[0].node.id);
-const context = await cg.buildContext('fix login bug', { maxNodes: 20, includeCode: true, format: 'markdown' });
-const impact = cg.getImpactRadius(results[0].node.id, 2);
+## Development
-cg.watch(); // auto-sync on file changes
-cg.unwatch(); // stop watching
-cg.close();
+```bash
+npm run build:codedelta
+npm run dev:codedelta # API :3847, web :5173, watches provider-runtime
+
+npm test -- packages/codedelta-graph-diff packages/codedelta-graph-subgraph \
+ packages/codedelta-impact-score packages/codedelta-server \
+ packages/codedelta-snapshot-manager packages/codedelta-trace-engine \
+ packages/codedelta-wiki-engine packages/codedelta-provider-runtime \
+ __tests__/codedelta
```
----
+Environment variables:
-## Configuration
-
-There isn't any — CodeGraph is zero-config, with **no config file** to write or
-keep in sync. Language support is automatic from the file extension; there's
-nothing to wire up per language.
-
-What it skips out of the box:
-
-- **Dependency, build, and cache directories** — `node_modules`, `vendor`,
- `dist`, `build`, `target`, `.venv`, `Pods`, `.next`, and the like across every
- [supported stack](#supported-languages) — so the graph is your code, not
- third-party noise. This holds even with no `.gitignore`.
-- **Anything in your `.gitignore`** — honored in git repos via git, and in
- non-git projects by reading `.gitignore` directly (root and nested).
-- **Files larger than 1 MB** — generated bundles, minified JS, vendored blobs.
-
-To keep something else out, add it to `.gitignore`. To pull a default-excluded
-directory back **in** (say you really do want a vendored dependency indexed),
-add a negation — `!vendor/`. The defaults apply uniformly, so committing a
-dependency or build directory doesn't force it into the graph; the `.gitignore`
-negation is the explicit opt-in.
-
-## Supported Platforms
-
-Every release ships a self-contained build (bundled Node runtime — nothing to
-compile) for all three desktop OSes, on both Intel/AMD (x64) and ARM (arm64):
-
-| Platform | Architectures | Install |
-|----------|---------------|---------|
-| Windows | x64, arm64 | PowerShell installer or npm |
-| macOS | x64, arm64 | shell installer or npm |
-| Linux | x64, arm64 | shell installer or npm |
-
-See [Get Started](#get-started) for the one-line install commands.
-
-## Supported Agents
-
-The interactive installer auto-detects and configures each of these — wiring up
-the MCP server and writing its instructions file:
-
-- **Claude Code**
-- **Cursor**
-- **Codex CLI**
-- **opencode**
-- **Hermes Agent**
-- **Gemini CLI**
-- **Antigravity IDE**
-- **Kiro**
-
-## Supported Languages
-
-| Language | Extension | Status |
-|----------|-----------|--------|
-| TypeScript | `.ts`, `.tsx` | Full support |
-| JavaScript | `.js`, `.jsx`, `.mjs` | Full support |
-| Python | `.py` | Full support |
-| Go | `.go` | Full support |
-| Rust | `.rs` | Full support |
-| Java | `.java` | Full support |
-| C# | `.cs` | Full support |
-| PHP | `.php` | Full support |
-| Ruby | `.rb` | Full support |
-| C | `.c`, `.h` | Full support |
-| C++ | `.cpp`, `.hpp`, `.cc` | Full support |
-| Objective-C | `.m`, `.mm`, `.h` | Partial support (classes, protocols, methods, `@property`, `#import`, message sends; `.mm` ObjC++ may parse incompletely) |
-| Swift | `.swift` | Full support |
-| Kotlin | `.kt`, `.kts` | Full support |
-| Scala | `.scala`, `.sc` | Full support (classes, traits, methods, type aliases, Scala 3 enums) |
-| Dart | `.dart` | Full support |
-| Svelte | `.svelte` | Full support (script extraction, Svelte 5 runes, SvelteKit routes) |
-| Vue | `.vue` | Full support (script + script-setup extraction, Nuxt page/API/middleware routes) |
-| Liquid | `.liquid` | Full support |
-| Pascal / Delphi | `.pas`, `.dpr`, `.dpk`, `.lpr` | Full support (classes, records, interfaces, enums, DFM/FMX form files) |
-| Lua | `.lua` | Full support (functions, methods with receivers, local variables, `require` imports, call edges) |
-| Luau | `.luau` | Full support (everything in Lua, plus `type`/`export type` aliases, typed signatures, and Roblox instance-path `require`) |
-
-## Troubleshooting
-
-**"CodeGraph not initialized"** — Run `codegraph init` in your project directory first.
-
-**Indexing is slow** — Check that `node_modules` and other large directories are excluded. Use `--quiet` to reduce output overhead.
-
-**MCP hits `database is locked`** — current builds shouldn't: CodeGraph bundles its own Node runtime and uses Node's built-in `node:sqlite` in WAL mode, where concurrent reads never block on a writer. If you still see it:
-
-- **You're on an old (pre-0.9) install.** Reinstall to get the bundled runtime — `curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh` (macOS/Linux), `irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex` (Windows), or `npm i -g @colbymchenry/codegraph@latest`.
-- **`codegraph status` shows `Journal:` other than `wal`** — WAL couldn't be enabled on this filesystem (common on network shares and WSL2 `/mnt`), so reads can block on writes. Move the project (with its `.codegraph/` folder) onto a local disk.
-
-**MCP server not connecting** — Ensure the project is initialized/indexed, verify the path in your MCP config, and check that `codegraph serve --mcp` works from the command line.
-
-**Missing symbols** — The MCP server auto-syncs on save (wait a couple seconds). Run `codegraph sync` manually if needed. Check that the file's language is supported and isn't inside a `.gitignore`d or default-excluded directory (e.g. `node_modules`, `dist`).
-
-## Star History
-
-
-
-
-
-
-
-
+| Variable | Default | Meaning |
+|----------|---------|---------|
+| `CODEDELTA_CACHE_DIR` | `.codedelta/` | Cache root |
+| `CODEDELTA_PORT` | `3847` | API port |
+| `CODEDELTA_MONOREPO_ROOT` | (monorepo root) | CodeGraph dist root; required for desktop bundle |
+| `CODEDELTA_STATIC_DIR` | — | Serve web UI from this directory (desktop production) |
+| `CODEDELTA_DESKTOP` | — | When `1`, default cache dir on macOS is Application Support |
+| `CODEDELTA_SNAPSHOT_TIMEOUT_MS` | `120000` | Snapshot build timeout |
+| `CODEDELTA_SNAPSHOT_MAX_NODES` | `50000` | Snapshot node cap |
## License
-MIT
+MIT — see [LICENSE](LICENSE).
----
+This repository combines:
-
+- **CodeGraph** (`src/`, CLI, MCP): Copyright (c) 2026 [Colby Mchenry](https://github.com/colbymchenry). Upstream: [@colbymchenry/codegraph](https://github.com/colbymchenry/codegraph).
+- **CodeDelta** (`packages/*`, `apps/web/`): Copyright (c) 2026 [ingeniousfrog](https://github.com/ingeniousfrog) and contributors.
-**Made for AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, and Kiro**
-
-[Report Bug](https://github.com/colbymchenry/codegraph/issues) · [Request Feature](https://github.com/colbymchenry/codegraph/issues)
-
-
+Both parts are licensed under the same MIT terms. Redistributions should retain the copyright notice in `LICENSE`.
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 000000000..5c3f9bd7c
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,471 @@
+
+
+
+
+# CodeDelta
+
+**本地优先、commit 感知的结构化代码智能**
+
+基于 [CodeGraph](https://github.com/colbymchenry/codegraph) · commit 级 diff、追溯、全景图与 Wiki
+
+
+
+[](https://github.com/ingeniousfrog/CodeDelta/actions/workflows/desktop-macos.yml)
+[](https://github.com/ingeniousfrog/CodeDelta/actions/workflows/desktop-windows.yml)
+[](https://github.com/ingeniousfrog/CodeDelta/releases)
+
+
+
+
+
+[⬇ 下载](#桌面版macos-与-windows) · [⚡ 快速开始](#快速开始) · [📖 English](README.md)
+
+
+
+[English](README.md) · **简体中文**
+
+
+
+---
+
+CodeDelta 回答 **「这个代码库是如何随时间演变的?」**:在每个 commit 上构建确定性的结构图,再基于该图做 diff、追溯、可视化与文档生成 —— 而不是仅依赖行级 diff 或文本分块 RAG。
+
+| | 能力 | 你能得到什么 |
+|:--:|------|-------------|
+| Δ | **[Delta View](#delta-view)** | 两个 commit 间的结构对比:符号、边、影响范围、冲击分数、文件 diff |
+| 🔍 | **[Trace View](#trace-view)** | 自然语言提问 → 排序后的候选 commit + 可验证证据 |
+| ◎ | **[Panorama](#panorama)** | 单 commit 交互式调用流图(或两 commit 间的 delta 着色叠加) |
+| 📄 | **[Wiki](#wiki-基于结构图的文档--ask)** | 按 commit 的文档、来自真实图边的 Mermaid、可选 LLM 叙述、**Ask this repo** |
+
+本仓库为 fork:**CodeGraph** 引擎在 [`src/`](src/)(CLI + MCP + tree-sitter 图)。**CodeDelta** 应用在 [`packages/`](packages/) 与 [`apps/web/`](apps/web/)(导入、时间线、delta、trace、panorama、wiki、设置 UI)。
+
+## 架构
+
+### 产品流程
+
+四个视图如何围绕同一仓库及其 commit 历史协作:
+
+```mermaid
+flowchart TB
+ Import["导入仓库\n(GitHub URL 或本地路径)"]
+ Timeline["Commit 时间线\n分支 + 历史"]
+
+ Import --> Timeline
+
+ Timeline --> Delta["Delta View\nbase → head 结构对比"]
+ Timeline --> Trace["Trace View\n哪个 commit 引入的?"]
+ Timeline --> Panorama["Panorama\n单 commit 调用流图"]
+ Timeline --> Wiki["Wiki\n生成文档 + Ask"]
+
+ Trace -->|"验证候选"| Delta
+ Delta -->|"Graph 标签 / 下钻"| Panorama
+ Wiki -->|"符号引用"| Panorama
+ Wiki --> Ask["Ask this repo\nLLM + 图证据白名单"]
+
+ Provider["设置 → Provider\n(Codex / OpenAI / 无)"]
+ Provider -.->|"可选叙述"| Trace
+ Provider -.->|"叙述 + Ask(必需)"| Wiki
+```
+
+### 项目结构(技术)
+
+```mermaid
+flowchart TB
+ subgraph Clients["客户端"]
+ Web["apps/web\nReact UI"]
+ Desktop["apps/desktop\nTauri 2 + 内置 Node 22"]
+ end
+
+ subgraph Server["packages/codedelta-server"]
+ API["REST API\n导入 · compare · trace · wiki · panorama"]
+ end
+
+ subgraph Engines["分析引擎"]
+ Snap["snapshot-manager\nworktree + 缓存"]
+ Diff["graph-diff · impact-score · delta-summary"]
+ Sub["graph-subgraph\nPanorama 布局"]
+ Trace["trace-engine"]
+ WikiEng["wiki-engine\n目录 · 页面 · Mermaid · Ask 检索"]
+ Prov["provider-runtime\n可选 LLM"]
+ end
+
+ subgraph Core["src/ — CodeGraph"]
+ CG["tree-sitter 提取 → SQLite\n每 commit exportGraph"]
+ end
+
+ subgraph Cache[".codedelta/(本地)"]
+ Repos["repos/ 克隆"]
+ Snaps["snapshots/ 每 commit"]
+ Wikis["wiki/ 生成的页面"]
+ end
+
+ Web --> API
+ Desktop --> API
+ API --> Snap
+ API --> Diff
+ API --> Sub
+ API --> Trace
+ API --> WikiEng
+ API --> Prov
+ Snap --> CG
+ Snap --> Repos
+ Diff --> Snaps
+ Sub --> Snaps
+ Trace --> Snaps
+ WikiEng --> Snaps
+ WikiEng --> Wikis
+ Trace --> Prov
+ WikiEng --> Prov
+```
+
+Wiki 生成借鉴 [DeepWiki](https://github.com/AsyncFuncAI/deepwiki-open),但采用 **图 grounding**:目录、图表与引用来自 CodeGraph 快照;LLM 仅在固定证据白名单之上添加叙述段落与 Ask 回答(不编造符号或边)。
+
+## 与 CodeGraph、Understand Anything 的区别
+
+| | **CodeGraph** | **CodeDelta** | **Understand Anything** |
+|---|---------------|---------------|-------------------------|
+| **核心问题** | 当前结构是什么?谁调用谁? | 两个 commit 之间结构如何变化?哪个 commit 可能引入了变化? | 这个仓库在讲什么?如何上手? |
+| **工作单元** | 当前工作区 / 已索引树 | `base commit → head commit`(+ 历史 trace) | 整库(或文档)快照 |
+| **输出** | MCP 工具、callers/callees、agent 上下文 | Delta 摘要、冲击分数、文件 diff、trace 候选 + 证据、**按 commit 的 Wiki + Ask** | 交互式全库图、导览、节点 plain-English 摘要 |
+| **分析方式** | 确定性 tree-sitter 图(SQLite) | 每 commit 快照上的同一套图,再做结构 diff | 多 agent 流水线 + LLM enriched 图 |
+| **AI 角色** | 可选(agent 经 MCP 用图) | Trace 可选(无 LLM 时确定性路径仍可用) | 解释与导览的核心 |
+| **最适合** | 日常编码 agent、重构、「X 在哪?」 | 发版审查、回归、「行为从哪次 commit 开始变?」 | 新人 onboarding、架构探索 |
+
+**组合使用:** CodeGraph(或 CodeDelta 内置引擎)看 **实时** 结构;CodeDelta 关注 **历史与 commit 级风险**;Understand Anything 适合 **整库导览** —— 不能替代 commit 间的结构 delta。
+
+## 功能
+
+### Delta View
+
+对比两个 commit(`Base` = 之前,`Head` = 之后):
+
+- 变更符号(函数、类、组件、路由)
+- 新增/删除的依赖边(`calls`、`imports`)
+- 图遍历得到的影响节点
+- 确定性冲击分数(严重度 + 说明)
+- Delta 摘要(主要变更区域、风险、建议审查顺序)
+- 文件级 unified diff 弹窗(点击文件或符号)
+- 每快照元数据(`codegraph` vs `fallback` 提取)
+- **Graph 标签** — head commit 上的 React Flow 调用树,节点/边按增删改着色
+
+### Panorama
+
+基于 CodeGraph 快照的交互式调用流图(React Flow):
+
+- **单 commit** — 顶层入口路由/组件/导出符号,按调用深度展开
+- **分支 + commit** 选择器;变更时自动重建
+- **下钻** — *从此处展开*、面包屑、返回 / 全部入口
+- **可分享 URL** — `?branch=&commit=&depth=&focusPath=` 保留下钻路径
+- **导出** — 由图数据生成 SVG 或高 DPI PNG(非 DOM 截图)
+- 可选 LLM 节点标签(非权威)
+- 可从 **Delta View → Graph**、**Trace View**、**Commit 时间线** 进入
+
+### Trace View
+
+用自然语言描述 bug、行为变化或问题:
+
+- 从历史中排序候选 commit(message、路径、符号、delta 信号)
+- 每个候选附带证据(有父 commit 时做 `previous → candidate` 对比)
+- 返回直接回答、置信度、不确定性与建议下一步
+- 跳转 Delta View 验证每个候选
+- 在候选 commit 上 **在 Panorama 中查看**(有 trace 符号时可高亮)
+
+**未配置任何 LLM** 时,Trace 仍返回候选、证据与影响范围(证据优先,不编造事实)。
+
+### Wiki(基于结构图的文档 + Ask)
+
+从 commit 时间线(或 `/repos/:id/wiki`)打开 **Wiki**。选择 commit → **Generate wiki** → 浏览目录、阅读页面,在侧栏使用 **Ask this repo**。
+
+借鉴 [DeepWiki](https://github.com/AsyncFuncAI/deepwiki-open),但基于 **CodeGraph 快照**,而非对原始文件的 embedding/RAG:
+
+| 层级 | 是否需要 LLM | 内容 |
+|------|-------------|------|
+| **结构** | 否 | 目录(概览、架构、顶层模块、路由/组件)、符号表、文件列表、README 摘录 |
+| **图表** | 否 | Mermaid 模块 import 图与调用流 — **来自真实边序列化**,不由模型编造 |
+| **叙述** | 可选 | 配置 Provider 后每页 prose(显示 “with LLM narration”) |
+| **Ask** | **是** | 对话式问答;词法 + 图检索 → 证据白名单 → 带引用的 LLM 回答 |
+
+- **引用** 链接到符号的文件/行范围;符号引用可在对应 commit 打开 **Panorama**
+- **Ask** 在问题无法匹配符号时,用入口点 + README 引导 LLM
+- 缓存于 `.codedelta/wiki////`;生成为带进度的后台任务
+- 引擎:[`packages/codedelta-wiki-engine/`](packages/codedelta-wiki-engine/)
+
+### Commit 时间线与导入
+
+- 导入公开 GitHub 仓库(`owner/repo` 或 URL)或 **本地 git 路径**
+- 浏览 commit;从时间线打开 Delta、Trace、Panorama 或 Wiki
+
+## CodeDelta 不是什么
+
+- **不是通用 Git GUI** — 无 merge UI 或分支工作流
+- **不是行 diff 优先工具** — 结构 delta 是产品核心;文本 diff 辅助审查
+- **不能替代 Understand Anything** — 无整库 onboarding 交互图或 LLM 导览
+
+## 快速开始
+
+需要 **Node.js 20–24** 与 **git**。
+
+```bash
+git clone https://github.com/ingeniousfrog/CodeDelta.git
+cd CodeDelta
+npm install
+npm run build:codedelta
+npm run dev:codedelta
+```
+
+打开 [http://localhost:3847](http://localhost:3847)(开发模式将 Vite UI 代理到 API 端口;[http://localhost:5173](http://localhost:5173) 也可用)。
+
+1. **导入** 仓库(GitHub URL 或本地路径)
+2. **Commit 时间线** — 选分支并浏览历史
+3. **Delta View** — 选择 `Base (before)` 与 `Head (after)` 后对比
+4. **Trace View** — 描述问题;查看候选并在 Delta 中验证
+5. **Panorama** — 选分支/commit 探索调用树;从任意入口或路由下钻
+6. **Wiki** — 选 commit、生成 wiki、浏览页面,并用带引用的 Ask 提问
+
+## 界面导览
+
+### 1) 导入仓库
+
+
+
+### 2) Commit 时间线
+
+
+
+### 3) Delta View(结构对比)
+
+
+
+### 4) Trace View(证据优先的来源分析)
+
+
+
+文件级弹窗示例:[`docs/images/delta-file-diff-modal.png`](docs/images/delta-file-diff-modal.png)
+
+API:[http://localhost:3847](http://localhost:3847)
+
+| 端点 | 说明 |
+|------|------|
+| `GET /api/health` | 健康检查 |
+| `GET /api/repos/:id/compare?base=&head=` | 两 commit 间结构 delta |
+| `GET /api/repos/:id/panorama?commit=&depth=&root=` | 单 commit 调用流图 |
+| `GET /api/repos/:id/panorama?base=&head=&depth=` | Delta 着色调用流图(head 树) |
+| `POST /api/repos/:id/panorama/enrich` | 可选 LLM 节点标签 |
+| `GET /api/repos/:id/diff?base=&head=&file=` | 单文件 unified diff |
+| `POST /api/repos/:id/trace` | Trace 问题 → 候选 + 证据 |
+| `POST /api/repos/:id/wiki/generate?commit=` | 开始 wiki 生成(后台任务) |
+| `GET /api/repos/:id/wiki/status?commit=` | 生成状态与进度 |
+| `GET /api/repos/:id/wiki/toc?commit=` | Wiki 目录 |
+| `GET /api/repos/:id/wiki/page?commit=§ion=` | 单页(markdown + 引用) |
+| `GET /api/repos/:id/wiki/asset?commit=&path=` | commit 上的 README / wiki 图片 |
+| `POST /api/repos/:id/wiki/ask` | 问题 → 基于 commit 图的带引用回答 |
+| `GET /api/settings/provider` | 当前 LLM Provider 设置 |
+| `GET /api/settings/provider/codex-status` | 本机 Codex CLI 登录状态 |
+
+## 为 Trace 与 Wiki 配置 Codex(可选)
+
+CodeDelta 可复用 **已有 Codex CLI 登录** —— 无需在 Web UI 粘贴 API Key。
+
+### 1. 使用 Codex CLI 登录
+
+```bash
+# 如未安装:https://github.com/openai/codex
+codex login
+```
+
+会在 `~/.codex/auth.json` 创建或更新 ChatGPT OAuth。可用 `CODEX_HOME` 覆盖目录。
+
+### 2. 在 CodeDelta 中选择 Codex
+
+1. 打开应用 → **Settings → Provider Settings**
+2. 选择 **Codex OAuth**
+3. 确认页面显示已检测到本机登录(`~/.codex/` 下路径)
+4. **Model** — 留空则使用 `~/.codex/config.toml` 中的 `model`,或覆盖(如 `gpt-5.5`)
+5. **Save settings**
+
+### 3. 运行 Trace
+
+打开 **Trace View**,输入具体问题(文件路径、符号名、配置名更有帮助),点击 **Run trace**。
+
+确定性结果始终会出现;若已配置 Codex,模型可能润色叙述。模型输出 **非权威** —— 以证据与 Delta 验证为准。同一 Provider 也用于 **Wiki** 页面叙述与 **Ask**(Ask 必须配置 Provider;无 LLM 时 wiki 页面仍可生成结构化内容)。
+
+### Codex 故障排查
+
+| 现象 | 处理 |
+|------|------|
+| “auth.json not found” | 在与 CodeDelta 服务同一机器上运行 `codex login` |
+| `HTTP 400` / 不支持的参数 | 拉取最新代码后重启 `npm run dev:codedelta`(Codex 后端 ≠ OpenAI API) |
+| `fetch failed` / 超时 | 检查网络/VPN;查看 `ENOTFOUND` / `ETIMEDOUT` |
+| AI 区域报错但候选正常 | 预期回退 —— 结构 trace 仍可用;修复 Codex 后重试 |
+| 改了 Provider 代码未生效 | `dev:codedelta` 启动时会 rebuild 包;重启 dev server |
+
+**其他 Provider:** **No AI**(默认)、**OpenAI API key**、或 **OpenAI 兼容** base URL + key。Anthropic 与 Ollama 尚未实现。
+
+## 本地缓存(`.codedelta/`)
+
+| 路径 | 用途 |
+|------|------|
+| `.codedelta/repos//` | 克隆或引用的仓库 |
+| `.codedelta/registry.json` | 导入注册表 |
+| `.codedelta/snapshots////` | 每 commit 结构快照 |
+| `.codedelta/wiki////` | 生成的 wiki(目录、页面、元数据) |
+| `.codedelta/settings.json` | Provider 设置 |
+
+快照在 compare/trace 时 **懒构建** —— 不会预索引全部历史。
+
+## 提取
+
+**主路径:** 每 commit 在隔离 git worktree 中运行 CodeGraph(`index` + `exportGraph`)。
+
+**回退:** CodeGraph 失败时使用最小 TS/JS 提取器;快照记录 `extractionMethod: "fallback"` 与警告。
+
+## 基于 CodeGraph
+
+[`src/`](src/) 为上游 CodeGraph 项目:
+
+- Tree-sitter → SQLite 知识图
+- CLI:`codegraph init`、`codegraph sync`、`codegraph serve --mcp`
+- 面向 agent 的 MCP 工具(search、callers、callees、trace、impact)
+
+若还需 agent 运行时 MCP(与 CodeDelta Web 应用独立),可在目标仓库初始化 CodeGraph:
+
+```bash
+npm run build
+npx codegraph init -i
+```
+
+## 项目目录
+
+```
+src/ # CodeGraph 核心(与上游兼容)
+packages/
+ codedelta-types/
+ codedelta-repo-manager/
+ codedelta-server/ # REST API
+ codedelta-snapshot-manager/
+ codedelta-graph-diff/
+ codedelta-graph-subgraph/ # Panorama 调用树 + 布局
+ codedelta-impact-score/
+ codedelta-delta-summary/
+ codedelta-trace-engine/
+ codedelta-wiki-engine/ # Wiki 目录/页面/Mermaid + Ask 检索
+ codedelta-provider-runtime/
+apps/web/ # React UI(Delta、Trace、Panorama、Wiki)
+apps/desktop/ # macOS / Windows 桌面壳(Tauri 2)
+```
+
+路线图与待定项:[docs/codedelta/ROADMAP.md](docs/codedelta/ROADMAP.md)。
+
+## 限制
+
+- 当前实践路径以 TypeScript/JavaScript 为主
+- Delta 与 trace:**仅 commit 对 commit**(尚无 PR/分支/工作区对比)
+- Codex:仅本机 CLI 会话(无浏览器内 OAuth)
+- Panorama 概览在大仓库上只显示 **顶层入口面** —— 用 *从此处展开* 下钻;稀疏图常因挂载点(`USE /api/*`)需展开才能看到路由内部
+- Panorama 导出为简化卡片布局(无 live *Expand*);缩放/clarity 优先 **SVG**
+- 点击符号打开 **文件** diff,非符号到 hunk 的精确映射
+
+## 桌面版(macOS 与 Windows)
+
+CodeDelta 提供 **桌面应用**([`apps/desktop/`](apps/desktop/))— Tauri 2 壳,内置 Node 22(供 CodeGraph `node:sqlite`)与 API 服务。终端用户无需单独安装 Node。
+
+**版本** 来自 `apps/desktop/src-tauri/tauri.conf.json`(当前 **0.2.1**)。macOS 与 Windows 安装包发布在同一 GitHub Release:[`codedelta-desktop-v0.2.1`](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1)。桌面包含 **Delta、Trace、Panorama、Wiki**(Ask 需配置 LLM Provider)。**v0.2.0 已撤回** —— 其 macOS DMG 为手动打包路径,体积大约多 40 MB 且无功能收益;请使用 v0.2.1。
+
+### 下载
+
+| 平台 | 文件 | 说明 |
+|------|------|------|
+| **macOS**(Apple Silicon) | [GitHub Releases](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1) → `CodeDelta_*_aarch64.dmg` | 未签名;若被拦截请右键 → 打开 |
+| **Windows**(x64) | [GitHub Releases](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1) → `CodeDelta_*_x64-setup.exe` | NSIS 安装包 |
+| macOS 镜像 | [百度网盘](https://pan.baidu.com/s/1FQxOgNHyvU1Y5EB34RpogQ?pwd=frog) · 提取码: `frog` | **仅旧版 v0.1.0** — 无 Wiki;请用 [GitHub Releases](https://github.com/ingeniousfrog/CodeDelta/releases/tag/codedelta-desktop-v0.2.1) 获取当前版本 |
+
+**安装(macOS):** 打开 dmg → 将 **CodeDelta** 拖入「应用程序」。
+
+若从 GitHub/Safari 下载后 macOS 提示 **「已损坏,无法打开」**,这是 Gatekeeper 对未签名应用的隔离 —— 应用本身未损坏。修复:
+
+```bash
+xattr -cr /Applications/CodeDelta.app
+```
+
+然后重新打开,或首次右键应用 → **打开**。(百度网盘下载也常需同样 `xattr` 步骤。)
+
+**安装(Windows):** 运行 setup `.exe` 并按向导完成。
+
+两平台均需在 `PATH` 中有 **git**。**请勿在使用已安装应用的同时运行 `npm run dev:desktop`** —— 两者均占用 3847 端口;dev server 仅 API 会导致空白 `Cannot GET /`。
+
+**运行时数据:** macOS `~/Library/Application Support/CodeDelta` · Windows `%APPDATA%\CodeDelta`
+
+### 从源码构建
+
+**要求:** 目标操作系统(macOS 或 Windows)、[Rust 1.88+](https://rustup.rs/)、仓库 dev 依赖(`npm ci`)。macOS 还需 Xcode Command Line Tools;Windows 需 [NSIS](https://nsis.sourceforge.io/)。
+
+```bash
+# 一次性:staging 内置 Node + 服务运行时(约 200MB,位于 apps/desktop/src-tauri/resources/runtime/)
+npm run stage:desktop
+
+# 构建 .app + .dmg(未签名;首次打开 Gatekeeper 可能提示)
+npm run build:desktop
+
+# 开发:API + Vite + Tauri 窗口(localhost:5173 + :3847,非 bundled runtime)
+npm run dev:desktop
+```
+
+输出:
+- macOS:`apps/desktop/src-tauri/target/release/bundle/dmg/CodeDelta_*_aarch64.dmg`
+- Windows:`apps/desktop/src-tauri/target/release/bundle/nsis/CodeDelta_*_x64-setup.exe`
+
+**`apps/desktop/` 目录:**
+
+```
+apps/desktop/
+ package.json # tauri dev / build:app 脚本
+ src-tauri/
+ tauri.conf.json # 窗口、bundle、内置资源
+ src/server.rs # 启动时 spawn bundled Node API
+ resources/runtime/ # 由 npm run stage:desktop 生成(gitignore)
+```
+
+**Git:** 若缺少 `git`,应用会显示横幅提示。
+
+| 变量 | 桌面版默认 | 含义 |
+|------|-----------|------|
+| `CODEDELTA_CACHE_DIR` | `~/Library/Application Support/CodeDelta` | 缓存根(Tauri 设置) |
+| `CODEDELTA_MONOREPO_ROOT` | bundled `runtime/app` | CodeGraph dist 根 |
+| `CODEDELTA_STATIC_DIR` | bundled `runtime/web-dist` | Web UI 静态文件 |
+| `CODEDELTA_DESKTOP` | `1` | 桌面模式标志 |
+
+## 开发
+
+```bash
+npm run build:codedelta
+npm run dev:codedelta # API :3847,web :5173,watch provider-runtime
+
+npm test -- packages/codedelta-graph-diff packages/codedelta-graph-subgraph \
+ packages/codedelta-impact-score packages/codedelta-server \
+ packages/codedelta-snapshot-manager packages/codedelta-trace-engine \
+ packages/codedelta-wiki-engine packages/codedelta-provider-runtime \
+ __tests__/codedelta
+```
+
+环境变量:
+
+| 变量 | 默认 | 含义 |
+|------|------|------|
+| `CODEDELTA_CACHE_DIR` | `.codedelta/` | 缓存根 |
+| `CODEDELTA_PORT` | `3847` | API 端口 |
+| `CODEDELTA_MONOREPO_ROOT` |(monorepo 根)| CodeGraph dist 根;桌面 bundle 必需 |
+| `CODEDELTA_STATIC_DIR` | — | 从此目录提供 Web UI(桌面生产环境) |
+| `CODEDELTA_DESKTOP` | — | 为 `1` 时 macOS 默认缓存目录为 Application Support |
+| `CODEDELTA_SNAPSHOT_TIMEOUT_MS` | `120000` | 快照构建超时 |
+| `CODEDELTA_SNAPSHOT_MAX_NODES` | `50000` | 快照节点上限 |
+
+## 许可证
+
+MIT — 见 [LICENSE](LICENSE)。
+
+本仓库包含:
+
+- **CodeGraph**(`src/`、CLI、MCP):Copyright (c) 2026 [Colby Mchenry](https://github.com/colbymchenry)。上游:[@colbymchenry/codegraph](https://github.com/colbymchenry/codegraph)。
+- **CodeDelta**(`packages/*`、`apps/web/`):Copyright (c) 2026 [ingeniousfrog](https://github.com/ingeniousfrog) 及贡献者。
+
+两部分均遵循相同 MIT 条款。再分发时请保留 `LICENSE` 中的版权声明。
diff --git a/__tests__/codedelta/panorama-export.test.ts b/__tests__/codedelta/panorama-export.test.ts
new file mode 100644
index 000000000..cf6afec06
--- /dev/null
+++ b/__tests__/codedelta/panorama-export.test.ts
@@ -0,0 +1,59 @@
+import { describe, expect, it } from 'vitest';
+import type { PanoramaGraph } from '@codedelta/types';
+import { buildPanoramaSvg } from '../../apps/web/src/lib/panorama-export';
+
+function sampleGraph(): PanoramaGraph {
+ return {
+ repoId: 'r1',
+ commit: 'abc1234567890',
+ commitShortHash: 'abc1234',
+ nodes: [
+ {
+ id: 'n1',
+ kind: 'route',
+ name: 'GET /',
+ qualifiedName: 'GET /',
+ filePath: 'src/app.ts',
+ startLine: 1,
+ endLine: 5,
+ commitShortHash: 'abc1234',
+ role: 'entry',
+ position: { x: 0, y: 0 },
+ },
+ {
+ id: 'n2',
+ kind: 'function',
+ name: 'handler',
+ qualifiedName: 'handler',
+ filePath: 'src/app.ts',
+ startLine: 10,
+ endLine: 20,
+ commitShortHash: 'abc1234',
+ role: 'leaf',
+ position: { x: 0, y: 240 },
+ },
+ ],
+ edges: [{ id: 'e1', source: 'n1', target: 'n2', kind: 'calls' }],
+ entryPoints: ['n1'],
+ layout: 'layered',
+ stats: { nodeCount: 2, edgeCount: 1, truncated: false },
+ };
+}
+
+describe('buildPanoramaSvg', () => {
+ it('produces valid SVG with nodes and edges', () => {
+ const svg = buildPanoramaSvg(sampleGraph());
+ expect(svg).toContain(' {
+ const base = buildPanoramaSvg(sampleGraph());
+ const scaled = buildPanoramaSvg(sampleGraph(), { renderScale: 2 });
+ const baseW = base.match(/width="(\d+)"/)?.[1];
+ const scaledW = scaled.match(/width="(\d+)"/)?.[1];
+ expect(Number(scaledW)).toBeGreaterThan(Number(baseW));
+ });
+});
diff --git a/__tests__/codedelta/panorama-focus.test.ts b/__tests__/codedelta/panorama-focus.test.ts
new file mode 100644
index 000000000..647c15316
--- /dev/null
+++ b/__tests__/codedelta/panorama-focus.test.ts
@@ -0,0 +1,35 @@
+import { describe, expect, it } from 'vitest';
+import {
+ buildFocusTrail,
+ focusAtTrailIndex,
+ parseFocusPathParam,
+ serializeFocusPath,
+} from '../../apps/web/src/lib/panorama-focus';
+
+describe('panorama-focus URL trail', () => {
+ it('round-trips a multi-hop focus path', () => {
+ const trail = buildFocusTrail(['', 'MainActivity.onCreate'], 'handleClick');
+ const encoded = serializeFocusPath(trail);
+ expect(encoded).toBeTruthy();
+ const parsed = parseFocusPathParam(encoded);
+ expect(parsed.root).toBe('handleClick');
+ expect(parsed.stack).toEqual(['', 'MainActivity.onCreate']);
+ });
+
+ it('navigates breadcrumb index back to overview', () => {
+ const next = focusAtTrailIndex(['', 'A'], 'B', 0);
+ expect(next).toEqual({ stack: [], root: '' });
+ });
+
+ it('navigates breadcrumb index to intermediate hop', () => {
+ const next = focusAtTrailIndex(['', 'A'], 'B', 1);
+ expect(next).toEqual({ stack: [''], root: 'A' });
+ });
+
+ it('encodes symbols with special characters', () => {
+ const trail = buildFocusTrail([], 'GET /api/users');
+ const encoded = serializeFocusPath(trail)!;
+ const parsed = parseFocusPathParam(encoded);
+ expect(parsed.root).toBe('GET /api/users');
+ });
+});
diff --git a/__tests__/codedelta/wiki-server.test.ts b/__tests__/codedelta/wiki-server.test.ts
new file mode 100644
index 000000000..8ed3de2b0
--- /dev/null
+++ b/__tests__/codedelta/wiki-server.test.ts
@@ -0,0 +1,181 @@
+import { execFileSync } from 'child_process';
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import request from 'supertest';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import { createApp } from '../../packages/codedelta-server/src';
+
+function run(cmd: string, cwd: string): void {
+ execFileSync('sh', ['-c', cmd], { cwd, stdio: 'pipe' });
+}
+
+async function waitForWikiReady(
+ app: ReturnType['app'],
+ repoId: string,
+ commit: string,
+ timeoutMs = 120_000,
+): Promise> {
+ const deadline = Date.now() + timeoutMs;
+ for (;;) {
+ const res = await request(app).get(`/api/repos/${repoId}/wiki/status?commit=${commit}`);
+ expect(res.status).toBe(200);
+ if (res.body.state === 'ready') return res.body;
+ if (res.body.state === 'error') {
+ throw new Error(`wiki generation failed: ${res.body.error}`);
+ }
+ if (Date.now() > deadline) throw new Error('timed out waiting for wiki generation');
+ await new Promise((resolve) => setTimeout(resolve, 200));
+ }
+}
+
+describe('codedelta-server wiki (none provider, deterministic path)', () => {
+ let tmpDir: string;
+ let cacheRoot: string;
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-wiki-'));
+ cacheRoot = path.join(tmpDir, '.codedelta');
+ run('git init -b main', tmpDir);
+ run('git config user.email "test@example.com"', tmpDir);
+ run('git config user.name "Test User"', tmpDir);
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), '# wiki demo\n\n\n\nDemo repository for wiki tests.\n');
+ fs.mkdirSync(path.join(tmpDir, 'docs'), { recursive: true });
+ // 1x1 PNG
+ fs.writeFileSync(
+ path.join(tmpDir, 'docs', 'badge.png'),
+ Buffer.from(
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
+ 'base64',
+ ),
+ );
+ fs.mkdirSync(path.join(tmpDir, 'src'), { recursive: true });
+ fs.writeFileSync(
+ path.join(tmpDir, 'src', 'auth.ts'),
+ [
+ 'export function login(user: string): boolean {',
+ ' return validate(user);',
+ '}',
+ '',
+ 'export function validate(user: string): boolean {',
+ ' return user.length > 0;',
+ '}',
+ '',
+ ].join('\n'),
+ );
+ fs.writeFileSync(
+ path.join(tmpDir, 'src', 'server.ts'),
+ [
+ "import { login } from './auth';",
+ '',
+ 'export function handleRequest(user: string): string {',
+ " return login(user) ? 'ok' : 'denied';",
+ '}',
+ '',
+ ].join('\n'),
+ );
+ run('git add . && git commit -m "initial commit"', tmpDir);
+ });
+
+ afterEach(() => {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('generates a wiki, serves toc/pages, and answers ask deterministically', async () => {
+ const { app } = createApp({ cacheRoot });
+ const importRes = await request(app).post('/api/repos/import').send({ source: 'local', input: tmpDir });
+ expect(importRes.status).toBe(201);
+ const repoId = importRes.body.id as string;
+ const commit = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: tmpDir, encoding: 'utf8' }).trim();
+
+ // Status before generation.
+ const absent = await request(app).get(`/api/repos/${repoId}/wiki/status?commit=${commit}`);
+ expect(absent.status).toBe(200);
+ expect(absent.body.state).toBe('absent');
+
+ // TOC before generation → 404 guidance.
+ const earlyToc = await request(app).get(`/api/repos/${repoId}/wiki/toc?commit=${commit}`);
+ expect(earlyToc.status).toBe(404);
+
+ // Kick off generation (background job).
+ const gen = await request(app).post(`/api/repos/${repoId}/wiki/generate?commit=${commit}`);
+ expect([200, 202]).toContain(gen.status);
+
+ const ready = await waitForWikiReady(app, repoId, commit);
+ expect(ready.llmUsed).toBe(false);
+
+ // Re-generate on a ready wiki is a no-op.
+ const regen = await request(app).post(`/api/repos/${repoId}/wiki/generate?commit=${commit}`);
+ expect(regen.status).toBe(200);
+ expect(regen.body.status).toBe('ready');
+
+ // TOC: overview + architecture first, then module sections.
+ const toc = await request(app).get(`/api/repos/${repoId}/wiki/toc?commit=${commit}`);
+ expect(toc.status).toBe(200);
+ const sections = toc.body.sections as Array<{ id: string; kind: string }>;
+ expect(sections[0].id).toBe('overview');
+ expect(sections[1].id).toBe('architecture');
+ expect(sections.length).toBeGreaterThanOrEqual(2);
+
+ // Overview page: markdown with README excerpt, citations array present.
+ const overview = await request(app).get(
+ `/api/repos/${repoId}/wiki/page?commit=${commit}§ion=overview`,
+ );
+ expect(overview.status).toBe(200);
+ expect(overview.body.markdown).toContain('# Overview');
+ expect(overview.body.markdown).toContain('/wiki/asset?');
+ expect(overview.body.markdown).toContain(encodeURIComponent('docs/badge.png'));
+ expect(overview.body.markdown).toContain('Demo repository for wiki tests.');
+ expect(Array.isArray(overview.body.citations)).toBe(true);
+
+ const asset = await request(app).get(
+ `/api/repos/${repoId}/wiki/asset?commit=${commit}&path=${encodeURIComponent('docs/badge.png')}`,
+ );
+ expect(asset.status).toBe(200);
+ expect(asset.headers['content-type']).toMatch(/image\/png/);
+ expect(asset.body.length).toBeGreaterThan(0);
+
+ // Every TOC section has a retrievable page.
+ for (const section of sections) {
+ const page = await request(app).get(
+ `/api/repos/${repoId}/wiki/page?commit=${commit}§ion=${section.id}`,
+ );
+ expect(page.status).toBe(200);
+ expect(typeof page.body.markdown).toBe('string');
+ expect(page.body.markdown.length).toBeGreaterThan(0);
+ }
+
+ // Unknown section → 404.
+ const missing = await request(app).get(
+ `/api/repos/${repoId}/wiki/page?commit=${commit}§ion=nope`,
+ );
+ expect(missing.status).toBe(404);
+
+ // Ask requires a configured LLM provider (default settings use "none").
+ const ask = await request(app)
+ .post(`/api/repos/${repoId}/wiki/ask`)
+ .send({ commit, question: 'how does login validate the user?' });
+ expect(ask.status).toBe(503);
+ expect(ask.body.error).toContain('Provider Settings');
+
+ // Ask validation errors.
+ const noQuestion = await request(app).post(`/api/repos/${repoId}/wiki/ask`).send({ commit });
+ expect(noQuestion.status).toBe(400);
+ const noCommit = await request(app)
+ .post(`/api/repos/${repoId}/wiki/ask`)
+ .send({ question: 'anything' });
+ expect(noCommit.status).toBe(400);
+ }, 180_000);
+
+ it('rejects generate without commit and unknown repo', async () => {
+ const { app } = createApp({ cacheRoot });
+ const importRes = await request(app).post('/api/repos/import').send({ source: 'local', input: tmpDir });
+ const repoId = importRes.body.id as string;
+
+ const noCommit = await request(app).post(`/api/repos/${repoId}/wiki/generate`);
+ expect(noCommit.status).toBe(400);
+
+ const badRepo = await request(app).post(`/api/repos/does-not-exist/wiki/generate?commit=abc`);
+ expect(badRepo.status).toBe(404);
+ });
+});
diff --git a/apps/desktop/.gitignore b/apps/desktop/.gitignore
new file mode 100644
index 000000000..8423a471e
--- /dev/null
+++ b/apps/desktop/.gitignore
@@ -0,0 +1,6 @@
+/target/
+/src-tauri/target/
+/src-tauri/src-tauri/
+/app-icon.png
+/app-icon.ppm
+/.cargo-home/
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
new file mode 100644
index 000000000..70d7963de
--- /dev/null
+++ b/apps/desktop/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "@codedelta/desktop",
+ "version": "0.1.0",
+ "private": true,
+ "description": "CodeDelta desktop shell (Tauri 2, macOS + Windows)",
+ "scripts": {
+ "dev": "tauri dev",
+ "build:app": "tauri build"
+ },
+ "devDependencies": {
+ "@tauri-apps/cli": "^2.5.0"
+ },
+ "license": "MIT"
+}
diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock
new file mode 100644
index 000000000..6b4e231fa
--- /dev/null
+++ b/apps/desktop/src-tauri/Cargo.lock
@@ -0,0 +1,5097 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "alloc-no-stdlib"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
+
+[[package]]
+name = "alloc-stdlib"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
+dependencies = [
+ "alloc-no-stdlib",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
+[[package]]
+name = "async-trait"
+version = "0.1.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "atk"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b"
+dependencies = [
+ "atk-sys",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "atk-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
+dependencies = [
+ "objc2",
+]
+
+[[package]]
+name = "blocking"
+version = "1.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
+[[package]]
+name = "brotli"
+version = "8.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+ "brotli-decompressor",
+]
+
+[[package]]
+name = "brotli-decompressor"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924"
+dependencies = [
+ "alloc-no-stdlib",
+ "alloc-stdlib",
+]
+
+[[package]]
+name = "bs58"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cairo-rs"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2"
+dependencies = [
+ "bitflags 2.11.1",
+ "cairo-sys-rs",
+ "glib",
+ "libc",
+ "once_cell",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "camino"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "cargo_toml"
+version = "0.22.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77"
+dependencies = [
+ "serde",
+ "toml 0.9.12+spec-1.1.0",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.62"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
+dependencies = [
+ "find-msvc-tools",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfb"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
+dependencies = [
+ "byteorder",
+ "fnv",
+ "uuid",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "chrono"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
+dependencies = [
+ "iana-time-zone",
+ "num-traits",
+ "serde",
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "codedelta-desktop"
+version = "0.1.0"
+dependencies = [
+ "dirs",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-build",
+ "tauri-plugin-single-instance",
+]
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "cookie"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
+dependencies = [
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
+dependencies = [
+ "bitflags 2.11.1",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
+dependencies = [
+ "bitflags 2.11.1",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "cssparser"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa",
+ "phf",
+ "smallvec",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "ctor"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98"
+dependencies = [
+ "ctor-proc-macro",
+ "dtor",
+]
+
+[[package]]
+name = "ctor-proc-macro"
+version = "0.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1"
+
+[[package]]
+name = "darling"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
+dependencies = [
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "dbus"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73"
+dependencies = [
+ "libc",
+ "libdbus-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+dependencies = [
+ "powerfmt",
+ "serde_core",
+]
+
+[[package]]
+name = "derive_more"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "dispatch2"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "libc",
+ "objc2",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "dlopen2"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4"
+dependencies = [
+ "dlopen2_derive",
+ "libc",
+ "once_cell",
+ "winapi",
+]
+
+[[package]]
+name = "dlopen2_derive"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "dom_query"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89"
+dependencies = [
+ "bit-set",
+ "cssparser",
+ "foldhash 0.2.0",
+ "html5ever",
+ "precomputed-hash",
+ "selectors",
+ "tendril",
+]
+
+[[package]]
+name = "dpi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "dtoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
+dependencies = [
+ "dtoa",
+]
+
+[[package]]
+name = "dtor"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4"
+dependencies = [
+ "dtor-proc-macro",
+]
+
+[[package]]
+name = "dtor-proc-macro"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
+
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
+
+[[package]]
+name = "embed-resource"
+version = "3.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb"
+dependencies = [
+ "cc",
+ "memchr",
+ "rustc_version",
+ "toml 1.1.2+spec-1.1.0",
+ "vswhom",
+ "winreg",
+]
+
+[[package]]
+name = "embed_plist"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+
+[[package]]
+name = "endi"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
+
+[[package]]
+name = "enumflags2"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.7.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "erased-serde"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec"
+dependencies = [
+ "serde",
+ "serde_core",
+ "typeid",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "field-offset"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
+dependencies = [
+ "memoffset",
+ "rustc_version",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "flate2"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+
+[[package]]
+name = "futures-lite"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "gdk"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691"
+dependencies = [
+ "cairo-rs",
+ "gdk-pixbuf",
+ "gdk-sys",
+ "gio",
+ "glib",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gdk-pixbuf"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec"
+dependencies = [
+ "gdk-pixbuf-sys",
+ "gio",
+ "glib",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gdk-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "gdkwayland-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69"
+dependencies = [
+ "gdk-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "gdkx11"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe"
+dependencies = [
+ "gdk",
+ "gdkx11-sys",
+ "gio",
+ "glib",
+ "libc",
+ "x11",
+]
+
+[[package]]
+name = "gdkx11-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d"
+dependencies = [
+ "gdk-sys",
+ "glib-sys",
+ "libc",
+ "system-deps",
+ "x11",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 5.3.0",
+ "wasip2",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi 6.0.0",
+ "wasip2",
+ "wasip3",
+]
+
+[[package]]
+name = "gio"
+version = "0.18.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys",
+ "glib",
+ "libc",
+ "once_cell",
+ "pin-project-lite",
+ "smallvec",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+ "winapi",
+]
+
+[[package]]
+name = "glib"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5"
+dependencies = [
+ "bitflags 2.11.1",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "memchr",
+ "once_cell",
+ "smallvec",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.18.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro-crate 2.0.2",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+
+[[package]]
+name = "gobject-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gtk"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a"
+dependencies = [
+ "atk",
+ "cairo-rs",
+ "field-offset",
+ "futures-channel",
+ "gdk",
+ "gdk-pixbuf",
+ "gio",
+ "glib",
+ "gtk-sys",
+ "gtk3-macros",
+ "libc",
+ "pango",
+ "pkg-config",
+]
+
+[[package]]
+name = "gtk-sys"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414"
+dependencies = [
+ "atk-sys",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "gtk3-macros"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d"
+dependencies = [
+ "proc-macro-crate 1.3.1",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash 0.1.5",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "html5ever"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2"
+dependencies = [
+ "log",
+ "markup5ever",
+]
+
+[[package]]
+name = "http"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "hyper"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "http",
+ "http-body",
+ "httparse",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core 0.62.2",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ico"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371"
+dependencies = [
+ "byteorder",
+ "png 0.17.16",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "utf8_iter",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+
+[[package]]
+name = "icu_properties"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+
+[[package]]
+name = "icu_provider"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "id-arena"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+ "serde",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.1",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "infer"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
+dependencies = [
+ "cfb",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "javascriptcore-rs"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc"
+dependencies = [
+ "bitflags 1.3.2",
+ "glib",
+ "javascriptcore-rs-sys",
+]
+
+[[package]]
+name = "javascriptcore-rs-sys"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys 0.3.1",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
+dependencies = [
+ "jni-sys 0.4.1",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
+dependencies = [
+ "jni-sys-macros",
+]
+
+[[package]]
+name = "jni-sys-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
+dependencies = [
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "json-patch"
+version = "3.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08"
+dependencies = [
+ "jsonptr",
+ "serde",
+ "serde_json",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "jsonptr"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "keyboard-types"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a"
+dependencies = [
+ "bitflags 2.11.1",
+ "serde",
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "leb128fmt"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
+
+[[package]]
+name = "libappindicator"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a"
+dependencies = [
+ "glib",
+ "gtk",
+ "gtk-sys",
+ "libappindicator-sys",
+ "log",
+]
+
+[[package]]
+name = "libappindicator-sys"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf"
+dependencies = [
+ "gtk-sys",
+ "libloading",
+ "once_cell",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+
+[[package]]
+name = "libdbus-sys"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043"
+dependencies = [
+ "pkg-config",
+]
+
+[[package]]
+name = "libloading"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+dependencies = [
+ "cfg-if",
+ "winapi",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "litemap"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
+
+[[package]]
+name = "markup5ever"
+version = "0.38.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862"
+dependencies = [
+ "log",
+ "tendril",
+ "web_atoms",
+]
+
+[[package]]
+name = "memchr"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "mio"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "muda"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47a2e3dff89cd322c66647942668faee0a2b1f88ea6cbb4d374b4a8d7e92528c"
+dependencies = [
+ "crossbeam-channel",
+ "dpi",
+ "gtk",
+ "keyboard-types",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-foundation",
+ "once_cell",
+ "png 0.18.1",
+ "serde",
+ "thiserror 2.0.18",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.11.1",
+ "jni-sys 0.3.1",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys 0.3.1",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "num-conv"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro-crate 3.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
+dependencies = [
+ "objc2-encode",
+ "objc2-exception-helper",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags 2.11.1",
+ "dispatch2",
+ "objc2",
+]
+
+[[package]]
+name = "objc2-core-graphics"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
+dependencies = [
+ "bitflags 2.11.1",
+ "dispatch2",
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-io-surface",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-text"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-exception-helper"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-io-surface"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2",
+ "objc2-cloud-kit",
+ "objc2-core-data",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-core-image",
+ "objc2-core-location",
+ "objc2-core-text",
+ "objc2-foundation",
+ "objc2-quartz-core",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-web-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "pango"
+version = "0.18.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4"
+dependencies = [
+ "gio",
+ "glib",
+ "libc",
+ "once_cell",
+ "pango-sys",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "parking"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "phf"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+ "serde",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737"
+dependencies = [
+ "fastrand",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "piper"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
+[[package]]
+name = "plist"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1"
+dependencies = [
+ "base64 0.22.1",
+ "indexmap 2.14.0",
+ "quick-xml",
+ "serde",
+ "time",
+]
+
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "png"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
+dependencies = [
+ "bitflags 2.11.1",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit 0.19.15",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24"
+dependencies = [
+ "toml_datetime 0.6.3",
+ "toml_edit 0.20.2",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
+dependencies = [
+ "toml_edit 0.25.12+spec-1.1.0",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.39.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
+dependencies = [
+ "getrandom 0.2.17",
+ "libredox",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "ref-cast"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "regex"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
+
+[[package]]
+name = "reqwest"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "js-sys",
+ "log",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "sync_wrapper",
+ "tokio",
+ "tokio-util",
+ "tower",
+ "tower-http",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-streams",
+ "web-sys",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags 2.11.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schemars"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615"
+dependencies = [
+ "dyn-clone",
+ "indexmap 1.9.3",
+ "schemars_derive",
+ "serde",
+ "serde_json",
+ "url",
+ "uuid",
+]
+
+[[package]]
+name = "schemars"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
+dependencies = [
+ "dyn-clone",
+ "ref-cast",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "schemars_derive"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde_derive_internals",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "selectors"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c"
+dependencies = [
+ "bitflags 2.11.1",
+ "cssparser",
+ "derive_more",
+ "log",
+ "new_debug_unreachable",
+ "phf",
+ "phf_codegen",
+ "precomputed-hash",
+ "rustc-hash",
+ "servo_arc",
+ "smallvec",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+dependencies = [
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-untagged"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058"
+dependencies = [
+ "erased-serde",
+ "serde",
+ "serde_core",
+ "typeid",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_repr"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_with"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2"
+dependencies = [
+ "base64 0.22.1",
+ "bs58",
+ "chrono",
+ "hex",
+ "indexmap 1.9.3",
+ "indexmap 2.14.0",
+ "schemars 0.9.0",
+ "schemars 1.2.1",
+ "serde_core",
+ "serde_json",
+ "serde_with_macros",
+ "time",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "3.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "serialize-to-javascript"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5"
+dependencies = [
+ "serde",
+ "serde_json",
+ "serialize-to-javascript-impl",
+]
+
+[[package]]
+name = "serialize-to-javascript-impl"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "servo_arc"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930"
+dependencies = [
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
+dependencies = [
+ "errno",
+ "libc",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+
+[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socket2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "softbuffer"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3"
+dependencies = [
+ "bytemuck",
+ "js-sys",
+ "ndk",
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation",
+ "objc2-quartz-core",
+ "raw-window-handle",
+ "redox_syscall",
+ "tracing",
+ "wasm-bindgen",
+ "web-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "soup3"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f"
+dependencies = [
+ "futures-channel",
+ "gio",
+ "glib",
+ "libc",
+ "soup3-sys",
+]
+
+[[package]]
+name = "soup3-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "string_cache"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
+dependencies = [
+ "new_debug_unreachable",
+ "parking_lot",
+ "phf_shared",
+ "precomputed-hash",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "swift-rs"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7"
+dependencies = [
+ "base64 0.21.7",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck 0.5.0",
+ "pkg-config",
+ "toml 0.8.2",
+ "version-compare",
+]
+
+[[package]]
+name = "tao"
+version = "0.35.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "core-foundation",
+ "core-graphics",
+ "crossbeam-channel",
+ "dbus",
+ "dispatch2",
+ "dlopen2",
+ "dpi",
+ "gdkwayland-sys",
+ "gdkx11-sys",
+ "gtk",
+ "jni",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-sys",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "objc2-ui-kit",
+ "once_cell",
+ "parking_lot",
+ "percent-encoding",
+ "raw-window-handle",
+ "tao-macros",
+ "unicode-segmentation",
+ "url",
+ "windows",
+ "windows-core 0.61.2",
+ "windows-version",
+ "x11-dl",
+]
+
+[[package]]
+name = "tao-macros"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "tauri"
+version = "2.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "437404997acf375d85f1177afa7e11bb971f274ed6a7b83a2a3e339015f4cc28"
+dependencies = [
+ "anyhow",
+ "bytes",
+ "cookie",
+ "dirs",
+ "dunce",
+ "embed_plist",
+ "getrandom 0.3.4",
+ "glob",
+ "gtk",
+ "heck 0.5.0",
+ "http",
+ "jni",
+ "libc",
+ "log",
+ "mime",
+ "muda",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "objc2-ui-kit",
+ "objc2-web-kit",
+ "percent-encoding",
+ "plist",
+ "raw-window-handle",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "serialize-to-javascript",
+ "swift-rs",
+ "tauri-build",
+ "tauri-macros",
+ "tauri-runtime",
+ "tauri-runtime-wry",
+ "tauri-utils",
+ "thiserror 2.0.18",
+ "tokio",
+ "tray-icon",
+ "url",
+ "webkit2gtk",
+ "webview2-com",
+ "window-vibrancy",
+ "windows",
+]
+
+[[package]]
+name = "tauri-build"
+version = "2.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4aa1f9055fc23919a54e4e125052bed16ed04aef0487086e758fe01a67b451c7"
+dependencies = [
+ "anyhow",
+ "cargo_toml",
+ "dirs",
+ "glob",
+ "heck 0.5.0",
+ "json-patch",
+ "schemars 0.8.22",
+ "semver",
+ "serde",
+ "serde_json",
+ "tauri-utils",
+ "tauri-winres",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-codegen"
+version = "2.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4a0319528a025a38c4078e7dae2c446f4e63620ddb0659a643ede1cb38f90e9"
+dependencies = [
+ "base64 0.22.1",
+ "brotli",
+ "ico",
+ "json-patch",
+ "plist",
+ "png 0.17.16",
+ "proc-macro2",
+ "quote",
+ "semver",
+ "serde",
+ "serde_json",
+ "sha2",
+ "syn 2.0.117",
+ "tauri-utils",
+ "thiserror 2.0.18",
+ "time",
+ "url",
+ "uuid",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-macros"
+version = "2.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae6cb4e3896c21d2f6da5b31251d2faea0153bba56ed0e970f918115dbee4924"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "tauri-codegen",
+ "tauri-utils",
+]
+
+[[package]]
+name = "tauri-plugin-single-instance"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c8f29386f5e9fdc699182388a33ee80a56de436d91b67459e86afef426282af"
+dependencies = [
+ "serde",
+ "serde_json",
+ "tauri",
+ "thiserror 2.0.18",
+ "tracing",
+ "windows-sys 0.60.2",
+ "zbus",
+]
+
+[[package]]
+name = "tauri-runtime"
+version = "2.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48222d7116c8807eaa6fe2f372e023fae125084e61e6eca6d70b7961cdf129ef"
+dependencies = [
+ "cookie",
+ "dpi",
+ "gtk",
+ "http",
+ "jni",
+ "objc2",
+ "objc2-ui-kit",
+ "objc2-web-kit",
+ "raw-window-handle",
+ "serde",
+ "serde_json",
+ "tauri-utils",
+ "thiserror 2.0.18",
+ "url",
+ "webkit2gtk",
+ "webview2-com",
+ "windows",
+]
+
+[[package]]
+name = "tauri-runtime-wry"
+version = "2.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9"
+dependencies = [
+ "gtk",
+ "http",
+ "jni",
+ "log",
+ "objc2",
+ "objc2-app-kit",
+ "once_cell",
+ "percent-encoding",
+ "raw-window-handle",
+ "softbuffer",
+ "tao",
+ "tauri-runtime",
+ "tauri-utils",
+ "url",
+ "webkit2gtk",
+ "webview2-com",
+ "windows",
+ "wry",
+]
+
+[[package]]
+name = "tauri-utils"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "092379df9a707631978e6c56b1bc2401d387f01e2d4a3c123360d167bbb9aa95"
+dependencies = [
+ "anyhow",
+ "brotli",
+ "cargo_metadata",
+ "ctor",
+ "dom_query",
+ "dunce",
+ "glob",
+ "http",
+ "infer",
+ "json-patch",
+ "log",
+ "memchr",
+ "phf",
+ "plist",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "schemars 0.8.22",
+ "semver",
+ "serde",
+ "serde-untagged",
+ "serde_json",
+ "serde_with",
+ "swift-rs",
+ "thiserror 2.0.18",
+ "toml 1.1.2+spec-1.1.0",
+ "url",
+ "urlpattern",
+ "uuid",
+ "walkdir",
+]
+
+[[package]]
+name = "tauri-winres"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6"
+dependencies = [
+ "dunce",
+ "embed-resource",
+ "toml 1.1.2+spec-1.1.0",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom 0.4.2",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tendril"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24"
+dependencies = [
+ "new_debug_unreachable",
+ "utf-8",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "time"
+version = "0.3.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
+
+[[package]]
+name = "time-macros"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "tokio"
+version = "1.52.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d"
+dependencies = [
+ "serde",
+ "serde_spanned 0.6.9",
+ "toml_datetime 0.6.3",
+ "toml_edit 0.20.2",
+]
+
+[[package]]
+name = "toml"
+version = "0.9.12+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863"
+dependencies = [
+ "indexmap 2.14.0",
+ "serde_core",
+ "serde_spanned 1.1.1",
+ "toml_datetime 0.7.5+spec-1.1.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow 0.7.15",
+]
+
+[[package]]
+name = "toml"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee"
+dependencies = [
+ "indexmap 2.14.0",
+ "serde_core",
+ "serde_spanned 1.1.1",
+ "toml_datetime 1.1.1+spec-1.1.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow 1.0.3",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "1.1.1+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
+dependencies = [
+ "indexmap 2.14.0",
+ "toml_datetime 0.6.3",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338"
+dependencies = [
+ "indexmap 2.14.0",
+ "serde",
+ "serde_spanned 0.6.9",
+ "toml_datetime 0.6.3",
+ "winnow 0.5.40",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.25.12+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
+dependencies = [
+ "indexmap 2.14.0",
+ "toml_datetime 1.1.1+spec-1.1.0",
+ "toml_parser",
+ "winnow 1.0.3",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
+dependencies = [
+ "winnow 1.0.3",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.1.1+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
+
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
+dependencies = [
+ "bitflags 2.11.1",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "url",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "tray-icon"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773"
+dependencies = [
+ "crossbeam-channel",
+ "dirs",
+ "libappindicator",
+ "muda",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-core-graphics",
+ "objc2-foundation",
+ "once_cell",
+ "png 0.18.1",
+ "serde",
+ "thiserror 2.0.18",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "typeid"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
+
+[[package]]
+name = "typenum"
+version = "1.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
+
+[[package]]
+name = "uds_windows"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "unic-char-property"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
+dependencies = [
+ "unic-char-range",
+]
+
+[[package]]
+name = "unic-char-range"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
+
+[[package]]
+name = "unic-common"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
+
+[[package]]
+name = "unic-ucd-ident"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
+dependencies = [
+ "unic-char-property",
+ "unic-char-range",
+ "unic-ucd-version",
+]
+
+[[package]]
+name = "unic-ucd-version"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
+dependencies = [
+ "unic-common",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
+name = "urlpattern"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
+dependencies = [
+ "regex",
+ "serde",
+ "unic-ucd-ident",
+ "url",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "uuid"
+version = "1.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76"
+dependencies = [
+ "getrandom 0.4.2",
+ "js-sys",
+ "serde_core",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "version-compare"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "vswhom"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
+dependencies = [
+ "libc",
+ "vswhom-sys",
+]
+
+[[package]]
+name = "vswhom-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.3+wasi-0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
+dependencies = [
+ "wit-bindgen 0.57.1",
+]
+
+[[package]]
+name = "wasip3"
+version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
+dependencies = [
+ "wit-bindgen 0.51.0",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.122"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasm-encoder"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
+dependencies = [
+ "leb128fmt",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-metadata"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
+dependencies = [
+ "anyhow",
+ "indexmap 2.14.0",
+ "wasm-encoder",
+ "wasmparser",
+]
+
+[[package]]
+name = "wasm-streams"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb"
+dependencies = [
+ "futures-util",
+ "js-sys",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "wasmparser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
+dependencies = [
+ "bitflags 2.11.1",
+ "hashbrown 0.15.5",
+ "indexmap 2.14.0",
+ "semver",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web_atoms"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538"
+dependencies = [
+ "phf",
+ "phf_codegen",
+ "string_cache",
+ "string_cache_codegen",
+]
+
+[[package]]
+name = "webkit2gtk"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-rs",
+ "gdk",
+ "gdk-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "gtk",
+ "gtk-sys",
+ "javascriptcore-rs",
+ "libc",
+ "once_cell",
+ "soup3",
+ "webkit2gtk-sys",
+]
+
+[[package]]
+name = "webkit2gtk-sys"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5"
+dependencies = [
+ "bitflags 1.3.2",
+ "cairo-sys-rs",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "gtk-sys",
+ "javascriptcore-rs-sys",
+ "libc",
+ "pkg-config",
+ "soup3-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "webview2-com"
+version = "0.38.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
+dependencies = [
+ "webview2-com-macros",
+ "webview2-com-sys",
+ "windows",
+ "windows-core 0.61.2",
+ "windows-implement",
+ "windows-interface",
+]
+
+[[package]]
+name = "webview2-com-macros"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "webview2-com-sys"
+version = "0.38.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
+dependencies = [
+ "thiserror 2.0.18",
+ "windows",
+ "windows-core 0.61.2",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "window-vibrancy"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c"
+dependencies = [
+ "objc2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-foundation",
+ "raw-window-handle",
+ "windows-sys 0.59.0",
+ "windows-version",
+]
+
+[[package]]
+name = "windows"
+version = "0.61.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
+dependencies = [
+ "windows-collections",
+ "windows-core 0.61.2",
+ "windows-future",
+ "windows-link 0.1.3",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
+dependencies = [
+ "windows-core 0.61.2",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.1.3",
+ "windows-result 0.3.4",
+ "windows-strings 0.4.2",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
+dependencies = [
+ "windows-core 0.61.2",
+ "windows-link 0.1.3",
+ "windows-threading",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-numerics"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
+dependencies = [
+ "windows-core 0.61.2",
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link 0.2.1",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows-threading"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-version"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631"
+dependencies = [
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winnow"
+version = "0.5.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winnow"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
+
+[[package]]
+name = "winnow"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "winreg"
+version = "0.55.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
+dependencies = [
+ "wit-bindgen-rust-macro",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
+[[package]]
+name = "wit-bindgen-core"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-bindgen-rust"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
+dependencies = [
+ "anyhow",
+ "heck 0.5.0",
+ "indexmap 2.14.0",
+ "prettyplease",
+ "syn 2.0.117",
+ "wasm-metadata",
+ "wit-bindgen-core",
+ "wit-component",
+]
+
+[[package]]
+name = "wit-bindgen-rust-macro"
+version = "0.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
+dependencies = [
+ "anyhow",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "wit-bindgen-core",
+ "wit-bindgen-rust",
+]
+
+[[package]]
+name = "wit-component"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
+dependencies = [
+ "anyhow",
+ "bitflags 2.11.1",
+ "indexmap 2.14.0",
+ "log",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "wasm-encoder",
+ "wasm-metadata",
+ "wasmparser",
+ "wit-parser",
+]
+
+[[package]]
+name = "wit-parser"
+version = "0.244.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
+dependencies = [
+ "anyhow",
+ "id-arena",
+ "indexmap 2.14.0",
+ "log",
+ "semver",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "unicode-xid",
+ "wasmparser",
+]
+
+[[package]]
+name = "writeable"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "wry"
+version = "0.55.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514"
+dependencies = [
+ "base64 0.22.1",
+ "block2",
+ "cookie",
+ "crossbeam-channel",
+ "dirs",
+ "dom_query",
+ "dpi",
+ "dunce",
+ "gdkx11",
+ "gtk",
+ "http",
+ "javascriptcore-rs",
+ "jni",
+ "libc",
+ "ndk",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-core-foundation",
+ "objc2-foundation",
+ "objc2-ui-kit",
+ "objc2-web-kit",
+ "once_cell",
+ "percent-encoding",
+ "raw-window-handle",
+ "sha2",
+ "soup3",
+ "tao-macros",
+ "thiserror 2.0.18",
+ "url",
+ "webkit2gtk",
+ "webkit2gtk-sys",
+ "webview2-com",
+ "windows",
+ "windows-core 0.61.2",
+ "windows-version",
+ "x11-dl",
+]
+
+[[package]]
+name = "x11"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "yoke"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "synstructure",
+]
+
+[[package]]
+name = "zbus"
+version = "5.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-lite",
+ "hex",
+ "libc",
+ "ordered-stream",
+ "rustix",
+ "serde",
+ "serde_repr",
+ "tracing",
+ "uds_windows",
+ "uuid",
+ "windows-sys 0.61.2",
+ "winnow 1.0.3",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff"
+dependencies = [
+ "proc-macro-crate 3.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "zbus_names",
+ "zvariant",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d"
+dependencies = [
+ "serde",
+ "winnow 1.0.3",
+ "zvariant",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "synstructure",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
+[[package]]
+name = "zvariant"
+version = "5.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "winnow 1.0.3",
+ "zvariant_derive",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda"
+dependencies = [
+ "proc-macro-crate 3.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "syn 2.0.117",
+ "winnow 1.0.3",
+]
diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml
new file mode 100644
index 000000000..e3085967e
--- /dev/null
+++ b/apps/desktop/src-tauri/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "codedelta-desktop"
+version = "0.1.0"
+description = "CodeDelta desktop application"
+authors = ["CodeDelta"]
+edition = "2021"
+
+[lib]
+name = "codedelta_desktop_lib"
+crate-type = ["staticlib", "cdylib", "rlib"]
+
+[build-dependencies]
+tauri-build = { version = "2", features = [] }
+
+[dependencies]
+tauri = { version = "2", features = [] }
+tauri-plugin-single-instance = "2"
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+dirs = "6"
diff --git a/apps/desktop/src-tauri/build.rs b/apps/desktop/src-tauri/build.rs
new file mode 100644
index 000000000..d860e1e6a
--- /dev/null
+++ b/apps/desktop/src-tauri/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ tauri_build::build()
+}
diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json
new file mode 100644
index 000000000..e86301aeb
--- /dev/null
+++ b/apps/desktop/src-tauri/capabilities/default.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://schema.tauri.app/config/2/capability",
+ "identifier": "default",
+ "description": "Default capabilities for CodeDelta desktop",
+ "windows": ["main"],
+ "permissions": [
+ "core:default",
+ "core:window:allow-set-focus",
+ "core:window:allow-show"
+ ]
+}
diff --git a/apps/desktop/src-tauri/gen/schemas/acl-manifests.json b/apps/desktop/src-tauri/gen/schemas/acl-manifests.json
new file mode 100644
index 000000000..0eebfc46b
--- /dev/null
+++ b/apps/desktop/src-tauri/gen/schemas/acl-manifests.json
@@ -0,0 +1 @@
+{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener","allow-supports-multiple-windows"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-supports-multiple-windows":{"identifier":"allow-supports-multiple-windows","description":"Enables the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":["supports_multiple_windows"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-supports-multiple-windows":{"identifier":"deny-supports-multiple-windows","description":"Denies the supports_multiple_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["supports_multiple_windows"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-icon-with-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-icon-with-as-template":{"identifier":"allow-set-icon-with-as-template","description":"Enables the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_with_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-icon-with-as-template":{"identifier":"deny-set-icon-with-as-template","description":"Denies the set_icon_with_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_with_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-activity-name","allow-scene-identifier","allow-internal-toggle-maximize"]},"permissions":{"allow-activity-name":{"identifier":"allow-activity-name","description":"Enables the activity_name command without any pre-configured scope.","commands":{"allow":["activity_name"],"deny":[]}},"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-scene-identifier":{"identifier":"allow-scene-identifier","description":"Enables the scene_identifier command without any pre-configured scope.","commands":{"allow":["scene_identifier"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-activity-name":{"identifier":"deny-activity-name","description":"Denies the activity_name command without any pre-configured scope.","commands":{"allow":[],"deny":["activity_name"]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-scene-identifier":{"identifier":"deny-scene-identifier","description":"Denies the scene_identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["scene_identifier"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null}}
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/gen/schemas/capabilities.json b/apps/desktop/src-tauri/gen/schemas/capabilities.json
new file mode 100644
index 000000000..de7561a47
--- /dev/null
+++ b/apps/desktop/src-tauri/gen/schemas/capabilities.json
@@ -0,0 +1 @@
+{"default":{"identifier":"default","description":"Default capabilities for CodeDelta desktop","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-set-focus","core:window:allow-show"]}}
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/gen/schemas/desktop-schema.json b/apps/desktop/src-tauri/gen/schemas/desktop-schema.json
new file mode 100644
index 000000000..328664596
--- /dev/null
+++ b/apps/desktop/src-tauri/gen/schemas/desktop-schema.json
@@ -0,0 +1,2292 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CapabilityFile",
+ "description": "Capability formats accepted in a capability file.",
+ "anyOf": [
+ {
+ "description": "A single capability.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Capability"
+ }
+ ]
+ },
+ {
+ "description": "A list of capabilities.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Capability"
+ }
+ },
+ {
+ "description": "A list of capabilities.",
+ "type": "object",
+ "required": [
+ "capabilities"
+ ],
+ "properties": {
+ "capabilities": {
+ "description": "The list of capabilities.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Capability"
+ }
+ }
+ }
+ }
+ ],
+ "definitions": {
+ "Capability": {
+ "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
+ "type": "object",
+ "required": [
+ "identifier",
+ "permissions"
+ ],
+ "properties": {
+ "identifier": {
+ "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`",
+ "type": "string"
+ },
+ "description": {
+ "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.",
+ "default": "",
+ "type": "string"
+ },
+ "remote": {
+ "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```",
+ "anyOf": [
+ {
+ "$ref": "#/definitions/CapabilityRemote"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "local": {
+ "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.",
+ "default": true,
+ "type": "boolean"
+ },
+ "windows": {
+ "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "webviews": {
+ "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "permissions": {
+ "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/PermissionEntry"
+ },
+ "uniqueItems": true
+ },
+ "platforms": {
+ "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/Target"
+ }
+ }
+ }
+ },
+ "CapabilityRemote": {
+ "description": "Configuration for remote URLs that are associated with the capability.",
+ "type": "object",
+ "required": [
+ "urls"
+ ],
+ "properties": {
+ "urls": {
+ "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "PermissionEntry": {
+ "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.",
+ "anyOf": [
+ {
+ "description": "Reference a permission or permission set by identifier.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Identifier"
+ }
+ ]
+ },
+ {
+ "description": "Reference a permission or permission set by identifier and extends its scope.",
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "identifier": {
+ "description": "Identifier of the permission or permission set.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Identifier"
+ }
+ ]
+ },
+ "allow": {
+ "description": "Data that defines what is allowed by the scope.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/Value"
+ }
+ },
+ "deny": {
+ "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/Value"
+ }
+ }
+ }
+ }
+ ],
+ "required": [
+ "identifier"
+ ]
+ }
+ ]
+ },
+ "Identifier": {
+ "description": "Permission identifier",
+ "oneOf": [
+ {
+ "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`",
+ "type": "string",
+ "const": "core:default",
+ "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
+ },
+ {
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
+ "type": "string",
+ "const": "core:app:default",
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
+ },
+ {
+ "description": "Enables the app_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-app-hide",
+ "markdownDescription": "Enables the app_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the app_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-app-show",
+ "markdownDescription": "Enables the app_show command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the bundle_type command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-bundle-type",
+ "markdownDescription": "Enables the bundle_type command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the default_window_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-default-window-icon",
+ "markdownDescription": "Enables the default_window_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-fetch-data-store-identifiers",
+ "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-identifier",
+ "markdownDescription": "Enables the identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-name",
+ "markdownDescription": "Enables the name command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the register_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-register-listener",
+ "markdownDescription": "Enables the register_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_data_store command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-remove-data-store",
+ "markdownDescription": "Enables the remove_data_store command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-remove-listener",
+ "markdownDescription": "Enables the remove_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_app_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-set-app-theme",
+ "markdownDescription": "Enables the set_app_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_dock_visibility command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-set-dock-visibility",
+ "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-supports-multiple-windows",
+ "markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the tauri_version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-tauri-version",
+ "markdownDescription": "Enables the tauri_version command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-version",
+ "markdownDescription": "Enables the version command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the app_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-app-hide",
+ "markdownDescription": "Denies the app_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the app_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-app-show",
+ "markdownDescription": "Denies the app_show command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the bundle_type command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-bundle-type",
+ "markdownDescription": "Denies the bundle_type command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the default_window_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-default-window-icon",
+ "markdownDescription": "Denies the default_window_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-fetch-data-store-identifiers",
+ "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-identifier",
+ "markdownDescription": "Denies the identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-name",
+ "markdownDescription": "Denies the name command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the register_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-register-listener",
+ "markdownDescription": "Denies the register_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_data_store command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-remove-data-store",
+ "markdownDescription": "Denies the remove_data_store command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-remove-listener",
+ "markdownDescription": "Denies the remove_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_app_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-set-app-theme",
+ "markdownDescription": "Denies the set_app_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_dock_visibility command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-set-dock-visibility",
+ "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-supports-multiple-windows",
+ "markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the tauri_version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-tauri-version",
+ "markdownDescription": "Denies the tauri_version command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-version",
+ "markdownDescription": "Denies the version command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`",
+ "type": "string",
+ "const": "core:event:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`"
+ },
+ {
+ "description": "Enables the emit command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-emit",
+ "markdownDescription": "Enables the emit command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the emit_to command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-emit-to",
+ "markdownDescription": "Enables the emit_to command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the listen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-listen",
+ "markdownDescription": "Enables the listen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unlisten command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-unlisten",
+ "markdownDescription": "Enables the unlisten command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the emit command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-emit",
+ "markdownDescription": "Denies the emit command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the emit_to command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-emit-to",
+ "markdownDescription": "Denies the emit_to command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the listen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-listen",
+ "markdownDescription": "Denies the listen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unlisten command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-unlisten",
+ "markdownDescription": "Denies the unlisten command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`",
+ "type": "string",
+ "const": "core:image:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`"
+ },
+ {
+ "description": "Enables the from_bytes command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-from-bytes",
+ "markdownDescription": "Enables the from_bytes command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the from_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-from-path",
+ "markdownDescription": "Enables the from_path command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-new",
+ "markdownDescription": "Enables the new command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the rgba command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-rgba",
+ "markdownDescription": "Enables the rgba command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-size",
+ "markdownDescription": "Enables the size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the from_bytes command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-from-bytes",
+ "markdownDescription": "Denies the from_bytes command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the from_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-from-path",
+ "markdownDescription": "Denies the from_path command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-new",
+ "markdownDescription": "Denies the new command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the rgba command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-rgba",
+ "markdownDescription": "Denies the rgba command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-size",
+ "markdownDescription": "Denies the size command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`",
+ "type": "string",
+ "const": "core:menu:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`"
+ },
+ {
+ "description": "Enables the append command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-append",
+ "markdownDescription": "Enables the append command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create_default command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-create-default",
+ "markdownDescription": "Enables the create_default command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the get command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-get",
+ "markdownDescription": "Enables the get command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the insert command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-insert",
+ "markdownDescription": "Enables the insert command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-is-checked",
+ "markdownDescription": "Enables the is_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-is-enabled",
+ "markdownDescription": "Enables the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the items command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-items",
+ "markdownDescription": "Enables the items command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-new",
+ "markdownDescription": "Enables the new command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the popup command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-popup",
+ "markdownDescription": "Enables the popup command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the prepend command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-prepend",
+ "markdownDescription": "Enables the prepend command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-remove",
+ "markdownDescription": "Enables the remove command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_at command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-remove-at",
+ "markdownDescription": "Enables the remove_at command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_accelerator command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-accelerator",
+ "markdownDescription": "Enables the set_accelerator command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_app_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-app-menu",
+ "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-help-menu-for-nsapp",
+ "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_window_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-window-menu",
+ "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-windows-menu-for-nsapp",
+ "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-checked",
+ "markdownDescription": "Enables the set_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-enabled",
+ "markdownDescription": "Enables the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-icon",
+ "markdownDescription": "Enables the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-text",
+ "markdownDescription": "Enables the set_text command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-text",
+ "markdownDescription": "Enables the text command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the append command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-append",
+ "markdownDescription": "Denies the append command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create_default command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-create-default",
+ "markdownDescription": "Denies the create_default command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-get",
+ "markdownDescription": "Denies the get command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the insert command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-insert",
+ "markdownDescription": "Denies the insert command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-is-checked",
+ "markdownDescription": "Denies the is_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-is-enabled",
+ "markdownDescription": "Denies the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the items command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-items",
+ "markdownDescription": "Denies the items command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-new",
+ "markdownDescription": "Denies the new command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the popup command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-popup",
+ "markdownDescription": "Denies the popup command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the prepend command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-prepend",
+ "markdownDescription": "Denies the prepend command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-remove",
+ "markdownDescription": "Denies the remove command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_at command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-remove-at",
+ "markdownDescription": "Denies the remove_at command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_accelerator command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-accelerator",
+ "markdownDescription": "Denies the set_accelerator command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_app_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-app-menu",
+ "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-help-menu-for-nsapp",
+ "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_window_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-window-menu",
+ "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-windows-menu-for-nsapp",
+ "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-checked",
+ "markdownDescription": "Denies the set_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-enabled",
+ "markdownDescription": "Denies the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-icon",
+ "markdownDescription": "Denies the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-text",
+ "markdownDescription": "Denies the set_text command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-text",
+ "markdownDescription": "Denies the text command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`",
+ "type": "string",
+ "const": "core:path:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`"
+ },
+ {
+ "description": "Enables the basename command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-basename",
+ "markdownDescription": "Enables the basename command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the dirname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-dirname",
+ "markdownDescription": "Enables the dirname command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the extname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-extname",
+ "markdownDescription": "Enables the extname command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_absolute command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-is-absolute",
+ "markdownDescription": "Enables the is_absolute command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the join command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-join",
+ "markdownDescription": "Enables the join command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the normalize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-normalize",
+ "markdownDescription": "Enables the normalize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the resolve command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-resolve",
+ "markdownDescription": "Enables the resolve command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the resolve_directory command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-resolve-directory",
+ "markdownDescription": "Enables the resolve_directory command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the basename command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-basename",
+ "markdownDescription": "Denies the basename command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the dirname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-dirname",
+ "markdownDescription": "Denies the dirname command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the extname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-extname",
+ "markdownDescription": "Denies the extname command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_absolute command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-is-absolute",
+ "markdownDescription": "Denies the is_absolute command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the join command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-join",
+ "markdownDescription": "Denies the join command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the normalize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-normalize",
+ "markdownDescription": "Denies the normalize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the resolve command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-resolve",
+ "markdownDescription": "Denies the resolve command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the resolve_directory command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-resolve-directory",
+ "markdownDescription": "Denies the resolve_directory command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`",
+ "type": "string",
+ "const": "core:resources:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`"
+ },
+ {
+ "description": "Enables the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:resources:allow-close",
+ "markdownDescription": "Enables the close command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:resources:deny-close",
+ "markdownDescription": "Denies the close command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
+ "type": "string",
+ "const": "core:tray:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
+ },
+ {
+ "description": "Enables the get_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-get-by-id",
+ "markdownDescription": "Enables the get_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-new",
+ "markdownDescription": "Enables the new command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-remove-by-id",
+ "markdownDescription": "Enables the remove_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon",
+ "markdownDescription": "Enables the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon-as-template",
+ "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon-with-as-template",
+ "markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-menu",
+ "markdownDescription": "Enables the set_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-show-menu-on-left-click",
+ "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_temp_dir_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-temp-dir-path",
+ "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-title",
+ "markdownDescription": "Enables the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_tooltip command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-tooltip",
+ "markdownDescription": "Enables the set_tooltip command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-visible",
+ "markdownDescription": "Enables the set_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-get-by-id",
+ "markdownDescription": "Denies the get_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-new",
+ "markdownDescription": "Denies the new command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-remove-by-id",
+ "markdownDescription": "Denies the remove_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon",
+ "markdownDescription": "Denies the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon-as-template",
+ "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon-with-as-template",
+ "markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-menu",
+ "markdownDescription": "Denies the set_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-show-menu-on-left-click",
+ "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_temp_dir_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-temp-dir-path",
+ "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-title",
+ "markdownDescription": "Denies the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_tooltip command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-tooltip",
+ "markdownDescription": "Denies the set_tooltip command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-visible",
+ "markdownDescription": "Denies the set_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`",
+ "type": "string",
+ "const": "core:webview:default",
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`"
+ },
+ {
+ "description": "Enables the clear_all_browsing_data command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-clear-all-browsing-data",
+ "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create_webview command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-create-webview",
+ "markdownDescription": "Enables the create_webview command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create_webview_window command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-create-webview-window",
+ "markdownDescription": "Enables the create_webview_window command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the get_all_webviews command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-get-all-webviews",
+ "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the internal_toggle_devtools command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-internal-toggle-devtools",
+ "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the print command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-print",
+ "markdownDescription": "Enables the print command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the reparent command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-reparent",
+ "markdownDescription": "Enables the reparent command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_auto_resize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-auto-resize",
+ "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-background-color",
+ "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-focus",
+ "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-position",
+ "markdownDescription": "Enables the set_webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-size",
+ "markdownDescription": "Enables the set_webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_zoom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-zoom",
+ "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-close",
+ "markdownDescription": "Enables the webview_close command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-hide",
+ "markdownDescription": "Enables the webview_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-position",
+ "markdownDescription": "Enables the webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-show",
+ "markdownDescription": "Enables the webview_show command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-size",
+ "markdownDescription": "Enables the webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the clear_all_browsing_data command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-clear-all-browsing-data",
+ "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create_webview command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-create-webview",
+ "markdownDescription": "Denies the create_webview command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create_webview_window command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-create-webview-window",
+ "markdownDescription": "Denies the create_webview_window command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_all_webviews command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-get-all-webviews",
+ "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the internal_toggle_devtools command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-internal-toggle-devtools",
+ "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the print command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-print",
+ "markdownDescription": "Denies the print command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the reparent command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-reparent",
+ "markdownDescription": "Denies the reparent command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_auto_resize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-auto-resize",
+ "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-background-color",
+ "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-focus",
+ "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-position",
+ "markdownDescription": "Denies the set_webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-size",
+ "markdownDescription": "Denies the set_webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_zoom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-zoom",
+ "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-close",
+ "markdownDescription": "Denies the webview_close command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-hide",
+ "markdownDescription": "Denies the webview_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-position",
+ "markdownDescription": "Denies the webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-show",
+ "markdownDescription": "Denies the webview_show command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-size",
+ "markdownDescription": "Denies the webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
+ "type": "string",
+ "const": "core:window:default",
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
+ },
+ {
+ "description": "Enables the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-activity-name",
+ "markdownDescription": "Enables the activity_name command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the available_monitors command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-available-monitors",
+ "markdownDescription": "Enables the available_monitors command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the center command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-center",
+ "markdownDescription": "Enables the center command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-close",
+ "markdownDescription": "Enables the close command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-create",
+ "markdownDescription": "Enables the create command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the current_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-current-monitor",
+ "markdownDescription": "Enables the current_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-cursor-position",
+ "markdownDescription": "Enables the cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the destroy command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-destroy",
+ "markdownDescription": "Enables the destroy command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the get_all_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-get-all-windows",
+ "markdownDescription": "Enables the get_all_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-hide",
+ "markdownDescription": "Enables the hide command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the inner_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-inner-position",
+ "markdownDescription": "Enables the inner_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the inner_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-inner-size",
+ "markdownDescription": "Enables the inner_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the internal_toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-internal-toggle-maximize",
+ "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-always-on-top",
+ "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-closable",
+ "markdownDescription": "Enables the is_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_decorated command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-decorated",
+ "markdownDescription": "Enables the is_decorated command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-enabled",
+ "markdownDescription": "Enables the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_focused command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-focused",
+ "markdownDescription": "Enables the is_focused command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-fullscreen",
+ "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-maximizable",
+ "markdownDescription": "Enables the is_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_maximized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-maximized",
+ "markdownDescription": "Enables the is_maximized command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-minimizable",
+ "markdownDescription": "Enables the is_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_minimized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-minimized",
+ "markdownDescription": "Enables the is_minimized command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-resizable",
+ "markdownDescription": "Enables the is_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-visible",
+ "markdownDescription": "Enables the is_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-maximize",
+ "markdownDescription": "Enables the maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the minimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-minimize",
+ "markdownDescription": "Enables the minimize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the monitor_from_point command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-monitor-from-point",
+ "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the outer_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-outer-position",
+ "markdownDescription": "Enables the outer_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the outer_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-outer-size",
+ "markdownDescription": "Enables the outer_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the primary_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-primary-monitor",
+ "markdownDescription": "Enables the primary_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the request_user_attention command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-request-user-attention",
+ "markdownDescription": "Enables the request_user_attention command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the scale_factor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-scale-factor",
+ "markdownDescription": "Enables the scale_factor command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-scene-identifier",
+ "markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_always_on_bottom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-always-on-bottom",
+ "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-always-on-top",
+ "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-background-color",
+ "markdownDescription": "Enables the set_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_badge_count command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-badge-count",
+ "markdownDescription": "Enables the set_badge_count command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_badge_label command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-badge-label",
+ "markdownDescription": "Enables the set_badge_label command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-closable",
+ "markdownDescription": "Enables the set_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_content_protected command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-content-protected",
+ "markdownDescription": "Enables the set_content_protected command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_grab command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-grab",
+ "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-icon",
+ "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-position",
+ "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-visible",
+ "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_decorations command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-decorations",
+ "markdownDescription": "Enables the set_decorations command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_effects command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-effects",
+ "markdownDescription": "Enables the set_effects command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-enabled",
+ "markdownDescription": "Enables the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-focus",
+ "markdownDescription": "Enables the set_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_focusable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-focusable",
+ "markdownDescription": "Enables the set_focusable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-fullscreen",
+ "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-icon",
+ "markdownDescription": "Enables the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-ignore-cursor-events",
+ "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_max_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-max-size",
+ "markdownDescription": "Enables the set_max_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-maximizable",
+ "markdownDescription": "Enables the set_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_min_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-min-size",
+ "markdownDescription": "Enables the set_min_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-minimizable",
+ "markdownDescription": "Enables the set_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_overlay_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-overlay-icon",
+ "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-position",
+ "markdownDescription": "Enables the set_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_progress_bar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-progress-bar",
+ "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-resizable",
+ "markdownDescription": "Enables the set_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_shadow command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-shadow",
+ "markdownDescription": "Enables the set_shadow command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_simple_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-simple-fullscreen",
+ "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-size",
+ "markdownDescription": "Enables the set_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_size_constraints command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-size-constraints",
+ "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_skip_taskbar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-skip-taskbar",
+ "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-theme",
+ "markdownDescription": "Enables the set_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-title",
+ "markdownDescription": "Enables the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_title_bar_style command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-title-bar-style",
+ "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-visible-on-all-workspaces",
+ "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-show",
+ "markdownDescription": "Enables the show command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the start_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-start-dragging",
+ "markdownDescription": "Enables the start_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the start_resize_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-start-resize-dragging",
+ "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-theme",
+ "markdownDescription": "Enables the theme command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-title",
+ "markdownDescription": "Enables the title command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-toggle-maximize",
+ "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unmaximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-unmaximize",
+ "markdownDescription": "Enables the unmaximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unminimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-unminimize",
+ "markdownDescription": "Enables the unminimize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-activity-name",
+ "markdownDescription": "Denies the activity_name command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the available_monitors command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-available-monitors",
+ "markdownDescription": "Denies the available_monitors command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the center command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-center",
+ "markdownDescription": "Denies the center command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-close",
+ "markdownDescription": "Denies the close command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-create",
+ "markdownDescription": "Denies the create command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the current_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-current-monitor",
+ "markdownDescription": "Denies the current_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-cursor-position",
+ "markdownDescription": "Denies the cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the destroy command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-destroy",
+ "markdownDescription": "Denies the destroy command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_all_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-get-all-windows",
+ "markdownDescription": "Denies the get_all_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-hide",
+ "markdownDescription": "Denies the hide command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the inner_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-inner-position",
+ "markdownDescription": "Denies the inner_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the inner_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-inner-size",
+ "markdownDescription": "Denies the inner_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the internal_toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-internal-toggle-maximize",
+ "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-always-on-top",
+ "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-closable",
+ "markdownDescription": "Denies the is_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_decorated command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-decorated",
+ "markdownDescription": "Denies the is_decorated command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-enabled",
+ "markdownDescription": "Denies the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_focused command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-focused",
+ "markdownDescription": "Denies the is_focused command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-fullscreen",
+ "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-maximizable",
+ "markdownDescription": "Denies the is_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_maximized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-maximized",
+ "markdownDescription": "Denies the is_maximized command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-minimizable",
+ "markdownDescription": "Denies the is_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_minimized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-minimized",
+ "markdownDescription": "Denies the is_minimized command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-resizable",
+ "markdownDescription": "Denies the is_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-visible",
+ "markdownDescription": "Denies the is_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-maximize",
+ "markdownDescription": "Denies the maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the minimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-minimize",
+ "markdownDescription": "Denies the minimize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the monitor_from_point command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-monitor-from-point",
+ "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the outer_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-outer-position",
+ "markdownDescription": "Denies the outer_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the outer_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-outer-size",
+ "markdownDescription": "Denies the outer_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the primary_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-primary-monitor",
+ "markdownDescription": "Denies the primary_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the request_user_attention command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-request-user-attention",
+ "markdownDescription": "Denies the request_user_attention command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the scale_factor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-scale-factor",
+ "markdownDescription": "Denies the scale_factor command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-scene-identifier",
+ "markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_always_on_bottom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-always-on-bottom",
+ "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-always-on-top",
+ "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-background-color",
+ "markdownDescription": "Denies the set_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_badge_count command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-badge-count",
+ "markdownDescription": "Denies the set_badge_count command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_badge_label command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-badge-label",
+ "markdownDescription": "Denies the set_badge_label command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-closable",
+ "markdownDescription": "Denies the set_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_content_protected command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-content-protected",
+ "markdownDescription": "Denies the set_content_protected command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_grab command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-grab",
+ "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-icon",
+ "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-position",
+ "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-visible",
+ "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_decorations command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-decorations",
+ "markdownDescription": "Denies the set_decorations command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_effects command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-effects",
+ "markdownDescription": "Denies the set_effects command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-enabled",
+ "markdownDescription": "Denies the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-focus",
+ "markdownDescription": "Denies the set_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_focusable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-focusable",
+ "markdownDescription": "Denies the set_focusable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-fullscreen",
+ "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-icon",
+ "markdownDescription": "Denies the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-ignore-cursor-events",
+ "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_max_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-max-size",
+ "markdownDescription": "Denies the set_max_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-maximizable",
+ "markdownDescription": "Denies the set_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_min_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-min-size",
+ "markdownDescription": "Denies the set_min_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-minimizable",
+ "markdownDescription": "Denies the set_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_overlay_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-overlay-icon",
+ "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-position",
+ "markdownDescription": "Denies the set_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_progress_bar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-progress-bar",
+ "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-resizable",
+ "markdownDescription": "Denies the set_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_shadow command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-shadow",
+ "markdownDescription": "Denies the set_shadow command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_simple_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-simple-fullscreen",
+ "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-size",
+ "markdownDescription": "Denies the set_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_size_constraints command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-size-constraints",
+ "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_skip_taskbar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-skip-taskbar",
+ "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-theme",
+ "markdownDescription": "Denies the set_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-title",
+ "markdownDescription": "Denies the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_title_bar_style command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-title-bar-style",
+ "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-visible-on-all-workspaces",
+ "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-show",
+ "markdownDescription": "Denies the show command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the start_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-start-dragging",
+ "markdownDescription": "Denies the start_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the start_resize_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-start-resize-dragging",
+ "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-theme",
+ "markdownDescription": "Denies the theme command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-title",
+ "markdownDescription": "Denies the title command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-toggle-maximize",
+ "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unmaximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-unmaximize",
+ "markdownDescription": "Denies the unmaximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unminimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-unminimize",
+ "markdownDescription": "Denies the unminimize command without any pre-configured scope."
+ }
+ ]
+ },
+ "Value": {
+ "description": "All supported ACL values.",
+ "anyOf": [
+ {
+ "description": "Represents a null JSON value.",
+ "type": "null"
+ },
+ {
+ "description": "Represents a [`bool`].",
+ "type": "boolean"
+ },
+ {
+ "description": "Represents a valid ACL [`Number`].",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Number"
+ }
+ ]
+ },
+ {
+ "description": "Represents a [`String`].",
+ "type": "string"
+ },
+ {
+ "description": "Represents a list of other [`Value`]s.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Value"
+ }
+ },
+ {
+ "description": "Represents a map of [`String`] keys to [`Value`]s.",
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/Value"
+ }
+ }
+ ]
+ },
+ "Number": {
+ "description": "A valid ACL number.",
+ "anyOf": [
+ {
+ "description": "Represents an [`i64`].",
+ "type": "integer",
+ "format": "int64"
+ },
+ {
+ "description": "Represents a [`f64`].",
+ "type": "number",
+ "format": "double"
+ }
+ ]
+ },
+ "Target": {
+ "description": "Platform target.",
+ "oneOf": [
+ {
+ "description": "MacOS.",
+ "type": "string",
+ "enum": [
+ "macOS"
+ ]
+ },
+ {
+ "description": "Windows.",
+ "type": "string",
+ "enum": [
+ "windows"
+ ]
+ },
+ {
+ "description": "Linux.",
+ "type": "string",
+ "enum": [
+ "linux"
+ ]
+ },
+ {
+ "description": "Android.",
+ "type": "string",
+ "enum": [
+ "android"
+ ]
+ },
+ {
+ "description": "iOS.",
+ "type": "string",
+ "enum": [
+ "iOS"
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/gen/schemas/macOS-schema.json b/apps/desktop/src-tauri/gen/schemas/macOS-schema.json
new file mode 100644
index 000000000..328664596
--- /dev/null
+++ b/apps/desktop/src-tauri/gen/schemas/macOS-schema.json
@@ -0,0 +1,2292 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "CapabilityFile",
+ "description": "Capability formats accepted in a capability file.",
+ "anyOf": [
+ {
+ "description": "A single capability.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Capability"
+ }
+ ]
+ },
+ {
+ "description": "A list of capabilities.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Capability"
+ }
+ },
+ {
+ "description": "A list of capabilities.",
+ "type": "object",
+ "required": [
+ "capabilities"
+ ],
+ "properties": {
+ "capabilities": {
+ "description": "The list of capabilities.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Capability"
+ }
+ }
+ }
+ }
+ ],
+ "definitions": {
+ "Capability": {
+ "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
+ "type": "object",
+ "required": [
+ "identifier",
+ "permissions"
+ ],
+ "properties": {
+ "identifier": {
+ "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`",
+ "type": "string"
+ },
+ "description": {
+ "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.",
+ "default": "",
+ "type": "string"
+ },
+ "remote": {
+ "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```",
+ "anyOf": [
+ {
+ "$ref": "#/definitions/CapabilityRemote"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
+ "local": {
+ "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.",
+ "default": true,
+ "type": "boolean"
+ },
+ "windows": {
+ "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nIf a window label matches any of the patterns in this list, the capability will be enabled on all the webviews of that window, regardless of the value of [`Self::webviews`].\n\nOn multiwebview windows, prefer specifying [`Self::webviews`] and omitting [`Self::windows`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "webviews": {
+ "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThe capability will be enabled on all the webviews whose label matches any of the patterns in this list, regardless of whether the webview's window label matches a pattern in [`Self::windows`].\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "permissions": {
+ "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/PermissionEntry"
+ },
+ "uniqueItems": true
+ },
+ "platforms": {
+ "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/Target"
+ }
+ }
+ }
+ },
+ "CapabilityRemote": {
+ "description": "Configuration for remote URLs that are associated with the capability.",
+ "type": "object",
+ "required": [
+ "urls"
+ ],
+ "properties": {
+ "urls": {
+ "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "PermissionEntry": {
+ "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.",
+ "anyOf": [
+ {
+ "description": "Reference a permission or permission set by identifier.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Identifier"
+ }
+ ]
+ },
+ {
+ "description": "Reference a permission or permission set by identifier and extends its scope.",
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "identifier": {
+ "description": "Identifier of the permission or permission set.",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Identifier"
+ }
+ ]
+ },
+ "allow": {
+ "description": "Data that defines what is allowed by the scope.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/Value"
+ }
+ },
+ "deny": {
+ "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "$ref": "#/definitions/Value"
+ }
+ }
+ }
+ }
+ ],
+ "required": [
+ "identifier"
+ ]
+ }
+ ]
+ },
+ "Identifier": {
+ "description": "Permission identifier",
+ "oneOf": [
+ {
+ "description": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`",
+ "type": "string",
+ "const": "core:default",
+ "markdownDescription": "Default core plugins set.\n#### This default permission set includes:\n\n- `core:path:default`\n- `core:event:default`\n- `core:window:default`\n- `core:webview:default`\n- `core:app:default`\n- `core:image:default`\n- `core:resources:default`\n- `core:menu:default`\n- `core:tray:default`"
+ },
+ {
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`",
+ "type": "string",
+ "const": "core:app:default",
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-version`\n- `allow-name`\n- `allow-tauri-version`\n- `allow-identifier`\n- `allow-bundle-type`\n- `allow-register-listener`\n- `allow-remove-listener`\n- `allow-supports-multiple-windows`"
+ },
+ {
+ "description": "Enables the app_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-app-hide",
+ "markdownDescription": "Enables the app_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the app_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-app-show",
+ "markdownDescription": "Enables the app_show command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the bundle_type command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-bundle-type",
+ "markdownDescription": "Enables the bundle_type command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the default_window_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-default-window-icon",
+ "markdownDescription": "Enables the default_window_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the fetch_data_store_identifiers command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-fetch-data-store-identifiers",
+ "markdownDescription": "Enables the fetch_data_store_identifiers command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-identifier",
+ "markdownDescription": "Enables the identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-name",
+ "markdownDescription": "Enables the name command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the register_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-register-listener",
+ "markdownDescription": "Enables the register_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_data_store command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-remove-data-store",
+ "markdownDescription": "Enables the remove_data_store command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-remove-listener",
+ "markdownDescription": "Enables the remove_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_app_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-set-app-theme",
+ "markdownDescription": "Enables the set_app_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_dock_visibility command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-set-dock-visibility",
+ "markdownDescription": "Enables the set_dock_visibility command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-supports-multiple-windows",
+ "markdownDescription": "Enables the supports_multiple_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the tauri_version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-tauri-version",
+ "markdownDescription": "Enables the tauri_version command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:allow-version",
+ "markdownDescription": "Enables the version command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the app_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-app-hide",
+ "markdownDescription": "Denies the app_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the app_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-app-show",
+ "markdownDescription": "Denies the app_show command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the bundle_type command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-bundle-type",
+ "markdownDescription": "Denies the bundle_type command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the default_window_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-default-window-icon",
+ "markdownDescription": "Denies the default_window_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the fetch_data_store_identifiers command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-fetch-data-store-identifiers",
+ "markdownDescription": "Denies the fetch_data_store_identifiers command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-identifier",
+ "markdownDescription": "Denies the identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-name",
+ "markdownDescription": "Denies the name command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the register_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-register-listener",
+ "markdownDescription": "Denies the register_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_data_store command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-remove-data-store",
+ "markdownDescription": "Denies the remove_data_store command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_listener command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-remove-listener",
+ "markdownDescription": "Denies the remove_listener command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_app_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-set-app-theme",
+ "markdownDescription": "Denies the set_app_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_dock_visibility command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-set-dock-visibility",
+ "markdownDescription": "Denies the set_dock_visibility command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the supports_multiple_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-supports-multiple-windows",
+ "markdownDescription": "Denies the supports_multiple_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the tauri_version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-tauri-version",
+ "markdownDescription": "Denies the tauri_version command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the version command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:app:deny-version",
+ "markdownDescription": "Denies the version command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`",
+ "type": "string",
+ "const": "core:event:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-listen`\n- `allow-unlisten`\n- `allow-emit`\n- `allow-emit-to`"
+ },
+ {
+ "description": "Enables the emit command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-emit",
+ "markdownDescription": "Enables the emit command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the emit_to command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-emit-to",
+ "markdownDescription": "Enables the emit_to command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the listen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-listen",
+ "markdownDescription": "Enables the listen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unlisten command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:allow-unlisten",
+ "markdownDescription": "Enables the unlisten command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the emit command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-emit",
+ "markdownDescription": "Denies the emit command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the emit_to command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-emit-to",
+ "markdownDescription": "Denies the emit_to command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the listen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-listen",
+ "markdownDescription": "Denies the listen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unlisten command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:event:deny-unlisten",
+ "markdownDescription": "Denies the unlisten command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`",
+ "type": "string",
+ "const": "core:image:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-from-bytes`\n- `allow-from-path`\n- `allow-rgba`\n- `allow-size`"
+ },
+ {
+ "description": "Enables the from_bytes command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-from-bytes",
+ "markdownDescription": "Enables the from_bytes command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the from_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-from-path",
+ "markdownDescription": "Enables the from_path command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-new",
+ "markdownDescription": "Enables the new command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the rgba command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-rgba",
+ "markdownDescription": "Enables the rgba command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:allow-size",
+ "markdownDescription": "Enables the size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the from_bytes command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-from-bytes",
+ "markdownDescription": "Denies the from_bytes command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the from_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-from-path",
+ "markdownDescription": "Denies the from_path command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-new",
+ "markdownDescription": "Denies the new command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the rgba command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-rgba",
+ "markdownDescription": "Denies the rgba command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:image:deny-size",
+ "markdownDescription": "Denies the size command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`",
+ "type": "string",
+ "const": "core:menu:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-append`\n- `allow-prepend`\n- `allow-insert`\n- `allow-remove`\n- `allow-remove-at`\n- `allow-items`\n- `allow-get`\n- `allow-popup`\n- `allow-create-default`\n- `allow-set-as-app-menu`\n- `allow-set-as-window-menu`\n- `allow-text`\n- `allow-set-text`\n- `allow-is-enabled`\n- `allow-set-enabled`\n- `allow-set-accelerator`\n- `allow-set-as-windows-menu-for-nsapp`\n- `allow-set-as-help-menu-for-nsapp`\n- `allow-is-checked`\n- `allow-set-checked`\n- `allow-set-icon`"
+ },
+ {
+ "description": "Enables the append command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-append",
+ "markdownDescription": "Enables the append command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create_default command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-create-default",
+ "markdownDescription": "Enables the create_default command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the get command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-get",
+ "markdownDescription": "Enables the get command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the insert command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-insert",
+ "markdownDescription": "Enables the insert command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-is-checked",
+ "markdownDescription": "Enables the is_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-is-enabled",
+ "markdownDescription": "Enables the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the items command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-items",
+ "markdownDescription": "Enables the items command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-new",
+ "markdownDescription": "Enables the new command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the popup command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-popup",
+ "markdownDescription": "Enables the popup command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the prepend command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-prepend",
+ "markdownDescription": "Enables the prepend command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-remove",
+ "markdownDescription": "Enables the remove command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_at command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-remove-at",
+ "markdownDescription": "Enables the remove_at command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_accelerator command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-accelerator",
+ "markdownDescription": "Enables the set_accelerator command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_app_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-app-menu",
+ "markdownDescription": "Enables the set_as_app_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-help-menu-for-nsapp",
+ "markdownDescription": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_window_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-window-menu",
+ "markdownDescription": "Enables the set_as_window_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-as-windows-menu-for-nsapp",
+ "markdownDescription": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-checked",
+ "markdownDescription": "Enables the set_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-enabled",
+ "markdownDescription": "Enables the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-icon",
+ "markdownDescription": "Enables the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-set-text",
+ "markdownDescription": "Enables the set_text command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:allow-text",
+ "markdownDescription": "Enables the text command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the append command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-append",
+ "markdownDescription": "Denies the append command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create_default command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-create-default",
+ "markdownDescription": "Denies the create_default command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-get",
+ "markdownDescription": "Denies the get command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the insert command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-insert",
+ "markdownDescription": "Denies the insert command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-is-checked",
+ "markdownDescription": "Denies the is_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-is-enabled",
+ "markdownDescription": "Denies the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the items command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-items",
+ "markdownDescription": "Denies the items command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-new",
+ "markdownDescription": "Denies the new command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the popup command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-popup",
+ "markdownDescription": "Denies the popup command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the prepend command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-prepend",
+ "markdownDescription": "Denies the prepend command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-remove",
+ "markdownDescription": "Denies the remove command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_at command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-remove-at",
+ "markdownDescription": "Denies the remove_at command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_accelerator command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-accelerator",
+ "markdownDescription": "Denies the set_accelerator command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_app_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-app-menu",
+ "markdownDescription": "Denies the set_as_app_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-help-menu-for-nsapp",
+ "markdownDescription": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_window_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-window-menu",
+ "markdownDescription": "Denies the set_as_window_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-as-windows-menu-for-nsapp",
+ "markdownDescription": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_checked command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-checked",
+ "markdownDescription": "Denies the set_checked command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-enabled",
+ "markdownDescription": "Denies the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-icon",
+ "markdownDescription": "Denies the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-set-text",
+ "markdownDescription": "Denies the set_text command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the text command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:menu:deny-text",
+ "markdownDescription": "Denies the text command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`",
+ "type": "string",
+ "const": "core:path:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-resolve-directory`\n- `allow-resolve`\n- `allow-normalize`\n- `allow-join`\n- `allow-dirname`\n- `allow-extname`\n- `allow-basename`\n- `allow-is-absolute`"
+ },
+ {
+ "description": "Enables the basename command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-basename",
+ "markdownDescription": "Enables the basename command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the dirname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-dirname",
+ "markdownDescription": "Enables the dirname command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the extname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-extname",
+ "markdownDescription": "Enables the extname command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_absolute command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-is-absolute",
+ "markdownDescription": "Enables the is_absolute command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the join command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-join",
+ "markdownDescription": "Enables the join command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the normalize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-normalize",
+ "markdownDescription": "Enables the normalize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the resolve command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-resolve",
+ "markdownDescription": "Enables the resolve command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the resolve_directory command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:allow-resolve-directory",
+ "markdownDescription": "Enables the resolve_directory command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the basename command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-basename",
+ "markdownDescription": "Denies the basename command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the dirname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-dirname",
+ "markdownDescription": "Denies the dirname command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the extname command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-extname",
+ "markdownDescription": "Denies the extname command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_absolute command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-is-absolute",
+ "markdownDescription": "Denies the is_absolute command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the join command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-join",
+ "markdownDescription": "Denies the join command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the normalize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-normalize",
+ "markdownDescription": "Denies the normalize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the resolve command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-resolve",
+ "markdownDescription": "Denies the resolve command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the resolve_directory command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:path:deny-resolve-directory",
+ "markdownDescription": "Denies the resolve_directory command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`",
+ "type": "string",
+ "const": "core:resources:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-close`"
+ },
+ {
+ "description": "Enables the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:resources:allow-close",
+ "markdownDescription": "Enables the close command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:resources:deny-close",
+ "markdownDescription": "Denies the close command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`",
+ "type": "string",
+ "const": "core:tray:default",
+ "markdownDescription": "Default permissions for the plugin, which enables all commands.\n#### This default permission set includes:\n\n- `allow-new`\n- `allow-get-by-id`\n- `allow-remove-by-id`\n- `allow-set-icon`\n- `allow-set-menu`\n- `allow-set-tooltip`\n- `allow-set-title`\n- `allow-set-visible`\n- `allow-set-temp-dir-path`\n- `allow-set-icon-as-template`\n- `allow-set-icon-with-as-template`\n- `allow-set-show-menu-on-left-click`"
+ },
+ {
+ "description": "Enables the get_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-get-by-id",
+ "markdownDescription": "Enables the get_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-new",
+ "markdownDescription": "Enables the new command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the remove_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-remove-by-id",
+ "markdownDescription": "Enables the remove_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon",
+ "markdownDescription": "Enables the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon-as-template",
+ "markdownDescription": "Enables the set_icon_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-icon-with-as-template",
+ "markdownDescription": "Enables the set_icon_with_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-menu",
+ "markdownDescription": "Enables the set_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-show-menu-on-left-click",
+ "markdownDescription": "Enables the set_show_menu_on_left_click command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_temp_dir_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-temp-dir-path",
+ "markdownDescription": "Enables the set_temp_dir_path command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-title",
+ "markdownDescription": "Enables the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_tooltip command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-tooltip",
+ "markdownDescription": "Enables the set_tooltip command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:allow-set-visible",
+ "markdownDescription": "Enables the set_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-get-by-id",
+ "markdownDescription": "Denies the get_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the new command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-new",
+ "markdownDescription": "Denies the new command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the remove_by_id command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-remove-by-id",
+ "markdownDescription": "Denies the remove_by_id command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon",
+ "markdownDescription": "Denies the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon-as-template",
+ "markdownDescription": "Denies the set_icon_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon_with_as_template command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-icon-with-as-template",
+ "markdownDescription": "Denies the set_icon_with_as_template command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_menu command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-menu",
+ "markdownDescription": "Denies the set_menu command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-show-menu-on-left-click",
+ "markdownDescription": "Denies the set_show_menu_on_left_click command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_temp_dir_path command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-temp-dir-path",
+ "markdownDescription": "Denies the set_temp_dir_path command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-title",
+ "markdownDescription": "Denies the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_tooltip command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-tooltip",
+ "markdownDescription": "Denies the set_tooltip command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:tray:deny-set-visible",
+ "markdownDescription": "Denies the set_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`",
+ "type": "string",
+ "const": "core:webview:default",
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-webviews`\n- `allow-webview-position`\n- `allow-webview-size`\n- `allow-internal-toggle-devtools`"
+ },
+ {
+ "description": "Enables the clear_all_browsing_data command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-clear-all-browsing-data",
+ "markdownDescription": "Enables the clear_all_browsing_data command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create_webview command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-create-webview",
+ "markdownDescription": "Enables the create_webview command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create_webview_window command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-create-webview-window",
+ "markdownDescription": "Enables the create_webview_window command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the get_all_webviews command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-get-all-webviews",
+ "markdownDescription": "Enables the get_all_webviews command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the internal_toggle_devtools command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-internal-toggle-devtools",
+ "markdownDescription": "Enables the internal_toggle_devtools command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the print command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-print",
+ "markdownDescription": "Enables the print command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the reparent command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-reparent",
+ "markdownDescription": "Enables the reparent command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_auto_resize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-auto-resize",
+ "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-background-color",
+ "markdownDescription": "Enables the set_webview_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-focus",
+ "markdownDescription": "Enables the set_webview_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-position",
+ "markdownDescription": "Enables the set_webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-size",
+ "markdownDescription": "Enables the set_webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_webview_zoom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-set-webview-zoom",
+ "markdownDescription": "Enables the set_webview_zoom command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-close",
+ "markdownDescription": "Enables the webview_close command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-hide",
+ "markdownDescription": "Enables the webview_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-position",
+ "markdownDescription": "Enables the webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-show",
+ "markdownDescription": "Enables the webview_show command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:allow-webview-size",
+ "markdownDescription": "Enables the webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the clear_all_browsing_data command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-clear-all-browsing-data",
+ "markdownDescription": "Denies the clear_all_browsing_data command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create_webview command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-create-webview",
+ "markdownDescription": "Denies the create_webview command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create_webview_window command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-create-webview-window",
+ "markdownDescription": "Denies the create_webview_window command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_all_webviews command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-get-all-webviews",
+ "markdownDescription": "Denies the get_all_webviews command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the internal_toggle_devtools command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-internal-toggle-devtools",
+ "markdownDescription": "Denies the internal_toggle_devtools command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the print command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-print",
+ "markdownDescription": "Denies the print command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the reparent command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-reparent",
+ "markdownDescription": "Denies the reparent command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_auto_resize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-auto-resize",
+ "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-background-color",
+ "markdownDescription": "Denies the set_webview_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-focus",
+ "markdownDescription": "Denies the set_webview_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-position",
+ "markdownDescription": "Denies the set_webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-size",
+ "markdownDescription": "Denies the set_webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_webview_zoom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-set-webview-zoom",
+ "markdownDescription": "Denies the set_webview_zoom command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-close",
+ "markdownDescription": "Denies the webview_close command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-hide",
+ "markdownDescription": "Denies the webview_hide command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-position",
+ "markdownDescription": "Denies the webview_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-show",
+ "markdownDescription": "Denies the webview_show command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the webview_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:webview:deny-webview-size",
+ "markdownDescription": "Denies the webview_size command without any pre-configured scope."
+ },
+ {
+ "description": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`",
+ "type": "string",
+ "const": "core:window:default",
+ "markdownDescription": "Default permissions for the plugin.\n#### This default permission set includes:\n\n- `allow-get-all-windows`\n- `allow-scale-factor`\n- `allow-inner-position`\n- `allow-outer-position`\n- `allow-inner-size`\n- `allow-outer-size`\n- `allow-is-fullscreen`\n- `allow-is-minimized`\n- `allow-is-maximized`\n- `allow-is-focused`\n- `allow-is-decorated`\n- `allow-is-resizable`\n- `allow-is-maximizable`\n- `allow-is-minimizable`\n- `allow-is-closable`\n- `allow-is-visible`\n- `allow-is-enabled`\n- `allow-title`\n- `allow-current-monitor`\n- `allow-primary-monitor`\n- `allow-monitor-from-point`\n- `allow-available-monitors`\n- `allow-cursor-position`\n- `allow-theme`\n- `allow-is-always-on-top`\n- `allow-activity-name`\n- `allow-scene-identifier`\n- `allow-internal-toggle-maximize`"
+ },
+ {
+ "description": "Enables the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-activity-name",
+ "markdownDescription": "Enables the activity_name command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the available_monitors command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-available-monitors",
+ "markdownDescription": "Enables the available_monitors command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the center command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-center",
+ "markdownDescription": "Enables the center command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-close",
+ "markdownDescription": "Enables the close command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the create command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-create",
+ "markdownDescription": "Enables the create command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the current_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-current-monitor",
+ "markdownDescription": "Enables the current_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-cursor-position",
+ "markdownDescription": "Enables the cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the destroy command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-destroy",
+ "markdownDescription": "Enables the destroy command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the get_all_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-get-all-windows",
+ "markdownDescription": "Enables the get_all_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-hide",
+ "markdownDescription": "Enables the hide command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the inner_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-inner-position",
+ "markdownDescription": "Enables the inner_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the inner_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-inner-size",
+ "markdownDescription": "Enables the inner_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the internal_toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-internal-toggle-maximize",
+ "markdownDescription": "Enables the internal_toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-always-on-top",
+ "markdownDescription": "Enables the is_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-closable",
+ "markdownDescription": "Enables the is_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_decorated command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-decorated",
+ "markdownDescription": "Enables the is_decorated command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-enabled",
+ "markdownDescription": "Enables the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_focused command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-focused",
+ "markdownDescription": "Enables the is_focused command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-fullscreen",
+ "markdownDescription": "Enables the is_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-maximizable",
+ "markdownDescription": "Enables the is_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_maximized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-maximized",
+ "markdownDescription": "Enables the is_maximized command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-minimizable",
+ "markdownDescription": "Enables the is_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_minimized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-minimized",
+ "markdownDescription": "Enables the is_minimized command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-resizable",
+ "markdownDescription": "Enables the is_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the is_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-is-visible",
+ "markdownDescription": "Enables the is_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-maximize",
+ "markdownDescription": "Enables the maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the minimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-minimize",
+ "markdownDescription": "Enables the minimize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the monitor_from_point command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-monitor-from-point",
+ "markdownDescription": "Enables the monitor_from_point command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the outer_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-outer-position",
+ "markdownDescription": "Enables the outer_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the outer_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-outer-size",
+ "markdownDescription": "Enables the outer_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the primary_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-primary-monitor",
+ "markdownDescription": "Enables the primary_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the request_user_attention command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-request-user-attention",
+ "markdownDescription": "Enables the request_user_attention command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the scale_factor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-scale-factor",
+ "markdownDescription": "Enables the scale_factor command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-scene-identifier",
+ "markdownDescription": "Enables the scene_identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_always_on_bottom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-always-on-bottom",
+ "markdownDescription": "Enables the set_always_on_bottom command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-always-on-top",
+ "markdownDescription": "Enables the set_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-background-color",
+ "markdownDescription": "Enables the set_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_badge_count command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-badge-count",
+ "markdownDescription": "Enables the set_badge_count command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_badge_label command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-badge-label",
+ "markdownDescription": "Enables the set_badge_label command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-closable",
+ "markdownDescription": "Enables the set_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_content_protected command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-content-protected",
+ "markdownDescription": "Enables the set_content_protected command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_grab command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-grab",
+ "markdownDescription": "Enables the set_cursor_grab command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-icon",
+ "markdownDescription": "Enables the set_cursor_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-position",
+ "markdownDescription": "Enables the set_cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_cursor_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-cursor-visible",
+ "markdownDescription": "Enables the set_cursor_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_decorations command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-decorations",
+ "markdownDescription": "Enables the set_decorations command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_effects command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-effects",
+ "markdownDescription": "Enables the set_effects command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-enabled",
+ "markdownDescription": "Enables the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-focus",
+ "markdownDescription": "Enables the set_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_focusable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-focusable",
+ "markdownDescription": "Enables the set_focusable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-fullscreen",
+ "markdownDescription": "Enables the set_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-icon",
+ "markdownDescription": "Enables the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-ignore-cursor-events",
+ "markdownDescription": "Enables the set_ignore_cursor_events command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_max_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-max-size",
+ "markdownDescription": "Enables the set_max_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-maximizable",
+ "markdownDescription": "Enables the set_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_min_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-min-size",
+ "markdownDescription": "Enables the set_min_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-minimizable",
+ "markdownDescription": "Enables the set_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_overlay_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-overlay-icon",
+ "markdownDescription": "Enables the set_overlay_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-position",
+ "markdownDescription": "Enables the set_position command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_progress_bar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-progress-bar",
+ "markdownDescription": "Enables the set_progress_bar command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-resizable",
+ "markdownDescription": "Enables the set_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_shadow command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-shadow",
+ "markdownDescription": "Enables the set_shadow command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_simple_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-simple-fullscreen",
+ "markdownDescription": "Enables the set_simple_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-size",
+ "markdownDescription": "Enables the set_size command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_size_constraints command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-size-constraints",
+ "markdownDescription": "Enables the set_size_constraints command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_skip_taskbar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-skip-taskbar",
+ "markdownDescription": "Enables the set_skip_taskbar command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-theme",
+ "markdownDescription": "Enables the set_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-title",
+ "markdownDescription": "Enables the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_title_bar_style command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-title-bar-style",
+ "markdownDescription": "Enables the set_title_bar_style command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-set-visible-on-all-workspaces",
+ "markdownDescription": "Enables the set_visible_on_all_workspaces command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-show",
+ "markdownDescription": "Enables the show command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the start_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-start-dragging",
+ "markdownDescription": "Enables the start_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the start_resize_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-start-resize-dragging",
+ "markdownDescription": "Enables the start_resize_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-theme",
+ "markdownDescription": "Enables the theme command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-title",
+ "markdownDescription": "Enables the title command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-toggle-maximize",
+ "markdownDescription": "Enables the toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unmaximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-unmaximize",
+ "markdownDescription": "Enables the unmaximize command without any pre-configured scope."
+ },
+ {
+ "description": "Enables the unminimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:allow-unminimize",
+ "markdownDescription": "Enables the unminimize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the activity_name command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-activity-name",
+ "markdownDescription": "Denies the activity_name command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the available_monitors command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-available-monitors",
+ "markdownDescription": "Denies the available_monitors command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the center command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-center",
+ "markdownDescription": "Denies the center command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the close command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-close",
+ "markdownDescription": "Denies the close command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the create command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-create",
+ "markdownDescription": "Denies the create command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the current_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-current-monitor",
+ "markdownDescription": "Denies the current_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-cursor-position",
+ "markdownDescription": "Denies the cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the destroy command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-destroy",
+ "markdownDescription": "Denies the destroy command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the get_all_windows command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-get-all-windows",
+ "markdownDescription": "Denies the get_all_windows command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the hide command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-hide",
+ "markdownDescription": "Denies the hide command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the inner_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-inner-position",
+ "markdownDescription": "Denies the inner_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the inner_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-inner-size",
+ "markdownDescription": "Denies the inner_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the internal_toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-internal-toggle-maximize",
+ "markdownDescription": "Denies the internal_toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-always-on-top",
+ "markdownDescription": "Denies the is_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-closable",
+ "markdownDescription": "Denies the is_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_decorated command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-decorated",
+ "markdownDescription": "Denies the is_decorated command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-enabled",
+ "markdownDescription": "Denies the is_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_focused command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-focused",
+ "markdownDescription": "Denies the is_focused command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-fullscreen",
+ "markdownDescription": "Denies the is_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-maximizable",
+ "markdownDescription": "Denies the is_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_maximized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-maximized",
+ "markdownDescription": "Denies the is_maximized command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-minimizable",
+ "markdownDescription": "Denies the is_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_minimized command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-minimized",
+ "markdownDescription": "Denies the is_minimized command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-resizable",
+ "markdownDescription": "Denies the is_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the is_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-is-visible",
+ "markdownDescription": "Denies the is_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-maximize",
+ "markdownDescription": "Denies the maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the minimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-minimize",
+ "markdownDescription": "Denies the minimize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the monitor_from_point command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-monitor-from-point",
+ "markdownDescription": "Denies the monitor_from_point command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the outer_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-outer-position",
+ "markdownDescription": "Denies the outer_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the outer_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-outer-size",
+ "markdownDescription": "Denies the outer_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the primary_monitor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-primary-monitor",
+ "markdownDescription": "Denies the primary_monitor command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the request_user_attention command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-request-user-attention",
+ "markdownDescription": "Denies the request_user_attention command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the scale_factor command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-scale-factor",
+ "markdownDescription": "Denies the scale_factor command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the scene_identifier command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-scene-identifier",
+ "markdownDescription": "Denies the scene_identifier command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_always_on_bottom command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-always-on-bottom",
+ "markdownDescription": "Denies the set_always_on_bottom command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_always_on_top command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-always-on-top",
+ "markdownDescription": "Denies the set_always_on_top command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_background_color command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-background-color",
+ "markdownDescription": "Denies the set_background_color command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_badge_count command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-badge-count",
+ "markdownDescription": "Denies the set_badge_count command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_badge_label command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-badge-label",
+ "markdownDescription": "Denies the set_badge_label command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_closable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-closable",
+ "markdownDescription": "Denies the set_closable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_content_protected command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-content-protected",
+ "markdownDescription": "Denies the set_content_protected command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_grab command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-grab",
+ "markdownDescription": "Denies the set_cursor_grab command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-icon",
+ "markdownDescription": "Denies the set_cursor_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-position",
+ "markdownDescription": "Denies the set_cursor_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_cursor_visible command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-cursor-visible",
+ "markdownDescription": "Denies the set_cursor_visible command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_decorations command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-decorations",
+ "markdownDescription": "Denies the set_decorations command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_effects command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-effects",
+ "markdownDescription": "Denies the set_effects command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_enabled command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-enabled",
+ "markdownDescription": "Denies the set_enabled command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_focus command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-focus",
+ "markdownDescription": "Denies the set_focus command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_focusable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-focusable",
+ "markdownDescription": "Denies the set_focusable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-fullscreen",
+ "markdownDescription": "Denies the set_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-icon",
+ "markdownDescription": "Denies the set_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-ignore-cursor-events",
+ "markdownDescription": "Denies the set_ignore_cursor_events command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_max_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-max-size",
+ "markdownDescription": "Denies the set_max_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_maximizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-maximizable",
+ "markdownDescription": "Denies the set_maximizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_min_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-min-size",
+ "markdownDescription": "Denies the set_min_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_minimizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-minimizable",
+ "markdownDescription": "Denies the set_minimizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_overlay_icon command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-overlay-icon",
+ "markdownDescription": "Denies the set_overlay_icon command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_position command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-position",
+ "markdownDescription": "Denies the set_position command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_progress_bar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-progress-bar",
+ "markdownDescription": "Denies the set_progress_bar command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_resizable command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-resizable",
+ "markdownDescription": "Denies the set_resizable command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_shadow command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-shadow",
+ "markdownDescription": "Denies the set_shadow command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_simple_fullscreen command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-simple-fullscreen",
+ "markdownDescription": "Denies the set_simple_fullscreen command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_size command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-size",
+ "markdownDescription": "Denies the set_size command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_size_constraints command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-size-constraints",
+ "markdownDescription": "Denies the set_size_constraints command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_skip_taskbar command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-skip-taskbar",
+ "markdownDescription": "Denies the set_skip_taskbar command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-theme",
+ "markdownDescription": "Denies the set_theme command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-title",
+ "markdownDescription": "Denies the set_title command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_title_bar_style command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-title-bar-style",
+ "markdownDescription": "Denies the set_title_bar_style command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-set-visible-on-all-workspaces",
+ "markdownDescription": "Denies the set_visible_on_all_workspaces command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the show command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-show",
+ "markdownDescription": "Denies the show command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the start_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-start-dragging",
+ "markdownDescription": "Denies the start_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the start_resize_dragging command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-start-resize-dragging",
+ "markdownDescription": "Denies the start_resize_dragging command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the theme command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-theme",
+ "markdownDescription": "Denies the theme command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the title command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-title",
+ "markdownDescription": "Denies the title command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the toggle_maximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-toggle-maximize",
+ "markdownDescription": "Denies the toggle_maximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unmaximize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-unmaximize",
+ "markdownDescription": "Denies the unmaximize command without any pre-configured scope."
+ },
+ {
+ "description": "Denies the unminimize command without any pre-configured scope.",
+ "type": "string",
+ "const": "core:window:deny-unminimize",
+ "markdownDescription": "Denies the unminimize command without any pre-configured scope."
+ }
+ ]
+ },
+ "Value": {
+ "description": "All supported ACL values.",
+ "anyOf": [
+ {
+ "description": "Represents a null JSON value.",
+ "type": "null"
+ },
+ {
+ "description": "Represents a [`bool`].",
+ "type": "boolean"
+ },
+ {
+ "description": "Represents a valid ACL [`Number`].",
+ "allOf": [
+ {
+ "$ref": "#/definitions/Number"
+ }
+ ]
+ },
+ {
+ "description": "Represents a [`String`].",
+ "type": "string"
+ },
+ {
+ "description": "Represents a list of other [`Value`]s.",
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/Value"
+ }
+ },
+ {
+ "description": "Represents a map of [`String`] keys to [`Value`]s.",
+ "type": "object",
+ "additionalProperties": {
+ "$ref": "#/definitions/Value"
+ }
+ }
+ ]
+ },
+ "Number": {
+ "description": "A valid ACL number.",
+ "anyOf": [
+ {
+ "description": "Represents an [`i64`].",
+ "type": "integer",
+ "format": "int64"
+ },
+ {
+ "description": "Represents a [`f64`].",
+ "type": "number",
+ "format": "double"
+ }
+ ]
+ },
+ "Target": {
+ "description": "Platform target.",
+ "oneOf": [
+ {
+ "description": "MacOS.",
+ "type": "string",
+ "enum": [
+ "macOS"
+ ]
+ },
+ {
+ "description": "Windows.",
+ "type": "string",
+ "enum": [
+ "windows"
+ ]
+ },
+ {
+ "description": "Linux.",
+ "type": "string",
+ "enum": [
+ "linux"
+ ]
+ },
+ {
+ "description": "Android.",
+ "type": "string",
+ "enum": [
+ "android"
+ ]
+ },
+ {
+ "description": "iOS.",
+ "type": "string",
+ "enum": [
+ "iOS"
+ ]
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/icons/128x128.png b/apps/desktop/src-tauri/icons/128x128.png
new file mode 100644
index 000000000..54ed88915
Binary files /dev/null and b/apps/desktop/src-tauri/icons/128x128.png differ
diff --git a/apps/desktop/src-tauri/icons/128x128@2x.png b/apps/desktop/src-tauri/icons/128x128@2x.png
new file mode 100644
index 000000000..efdb7d341
Binary files /dev/null and b/apps/desktop/src-tauri/icons/128x128@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/32x32.png b/apps/desktop/src-tauri/icons/32x32.png
new file mode 100644
index 000000000..4f7f95de9
Binary files /dev/null and b/apps/desktop/src-tauri/icons/32x32.png differ
diff --git a/apps/desktop/src-tauri/icons/64x64.png b/apps/desktop/src-tauri/icons/64x64.png
new file mode 100644
index 000000000..dc0309ba4
Binary files /dev/null and b/apps/desktop/src-tauri/icons/64x64.png differ
diff --git a/apps/desktop/src-tauri/icons/Square107x107Logo.png b/apps/desktop/src-tauri/icons/Square107x107Logo.png
new file mode 100644
index 000000000..974c98d36
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square107x107Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square142x142Logo.png b/apps/desktop/src-tauri/icons/Square142x142Logo.png
new file mode 100644
index 000000000..aa9fdd25d
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square142x142Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square150x150Logo.png b/apps/desktop/src-tauri/icons/Square150x150Logo.png
new file mode 100644
index 000000000..8de90d0d2
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square150x150Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square284x284Logo.png b/apps/desktop/src-tauri/icons/Square284x284Logo.png
new file mode 100644
index 000000000..67f8eb019
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square284x284Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square30x30Logo.png b/apps/desktop/src-tauri/icons/Square30x30Logo.png
new file mode 100644
index 000000000..98585f518
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square30x30Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square310x310Logo.png b/apps/desktop/src-tauri/icons/Square310x310Logo.png
new file mode 100644
index 000000000..cb6f0aa1e
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square310x310Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square44x44Logo.png b/apps/desktop/src-tauri/icons/Square44x44Logo.png
new file mode 100644
index 000000000..1903e1f6a
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square44x44Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square71x71Logo.png b/apps/desktop/src-tauri/icons/Square71x71Logo.png
new file mode 100644
index 000000000..eb6dfe568
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square71x71Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/Square89x89Logo.png b/apps/desktop/src-tauri/icons/Square89x89Logo.png
new file mode 100644
index 000000000..1bac4bc44
Binary files /dev/null and b/apps/desktop/src-tauri/icons/Square89x89Logo.png differ
diff --git a/apps/desktop/src-tauri/icons/StoreLogo.png b/apps/desktop/src-tauri/icons/StoreLogo.png
new file mode 100644
index 000000000..f69e35b52
Binary files /dev/null and b/apps/desktop/src-tauri/icons/StoreLogo.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/apps/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..2ffbf24b6
--- /dev/null
+++ b/apps/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..777589137
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..098de52d4
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..30e7a4164
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..5faf8182c
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..fbd26b10c
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..0b7a6ef66
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..15f557b09
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..44884ef70
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..0b435c6ae
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..a8e7e6585
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..fa2c80303
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..a3c133d9f
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..6f293b2ec
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..2d56e1110
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..8d615b513
Binary files /dev/null and b/apps/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/apps/desktop/src-tauri/icons/android/values/ic_launcher_background.xml b/apps/desktop/src-tauri/icons/android/values/ic_launcher_background.xml
new file mode 100644
index 000000000..ea9c223a6
--- /dev/null
+++ b/apps/desktop/src-tauri/icons/android/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+
+
+ #fff
+
\ No newline at end of file
diff --git a/apps/desktop/src-tauri/icons/icon.icns b/apps/desktop/src-tauri/icons/icon.icns
new file mode 100644
index 000000000..87b93649e
Binary files /dev/null and b/apps/desktop/src-tauri/icons/icon.icns differ
diff --git a/apps/desktop/src-tauri/icons/icon.ico b/apps/desktop/src-tauri/icons/icon.ico
new file mode 100644
index 000000000..93bf2b06a
Binary files /dev/null and b/apps/desktop/src-tauri/icons/icon.ico differ
diff --git a/apps/desktop/src-tauri/icons/icon.png b/apps/desktop/src-tauri/icons/icon.png
new file mode 100644
index 000000000..470af298c
Binary files /dev/null and b/apps/desktop/src-tauri/icons/icon.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png
new file mode 100644
index 000000000..605391e4b
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png
new file mode 100644
index 000000000..232f9f89a
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png
new file mode 100644
index 000000000..232f9f89a
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png
new file mode 100644
index 000000000..e52560d7d
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png
new file mode 100644
index 000000000..8a116d411
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png
new file mode 100644
index 000000000..e2f08b32d
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png
new file mode 100644
index 000000000..e2f08b32d
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png
new file mode 100644
index 000000000..bc4b34b4a
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png
new file mode 100644
index 000000000..232f9f89a
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png
new file mode 100644
index 000000000..8a1d9ca79
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png
new file mode 100644
index 000000000..8a1d9ca79
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png
new file mode 100644
index 000000000..436fbcecc
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-512@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-512@2x.png
new file mode 100644
index 000000000..ae6520cc1
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-512@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png
new file mode 100644
index 000000000..436fbcecc
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png
new file mode 100644
index 000000000..6044e65fd
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png
new file mode 100644
index 000000000..465b506a9
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png
new file mode 100644
index 000000000..2e8d8c37d
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png differ
diff --git a/apps/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/apps/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png
new file mode 100644
index 000000000..bc3a1d646
Binary files /dev/null and b/apps/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ
diff --git a/apps/desktop/src-tauri/rust-toolchain.toml b/apps/desktop/src-tauri/rust-toolchain.toml
new file mode 100644
index 000000000..ac83f8ed0
--- /dev/null
+++ b/apps/desktop/src-tauri/rust-toolchain.toml
@@ -0,0 +1,3 @@
+[toolchain]
+channel = "1.88"
+components = ["rustfmt", "clippy"]
diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs
new file mode 100644
index 000000000..0f0fb3d2a
--- /dev/null
+++ b/apps/desktop/src-tauri/src/lib.rs
@@ -0,0 +1,36 @@
+mod server;
+
+use tauri::{Manager, RunEvent, WindowEvent};
+
+#[cfg_attr(mobile, tauri::mobile_entry_point)]
+pub fn run() {
+ tauri::Builder::default()
+ .plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
+ if let Some(window) = app.get_webview_window("main") {
+ let _ = window.show();
+ let _ = window.set_focus();
+ }
+ }))
+ .setup(|app| {
+ if let Err(err) = server::start(app.handle()) {
+ if let Some(hint) = server::port_in_use_hint() {
+ eprintln!("{hint}");
+ }
+ eprintln!("CodeDelta startup failed: {err}");
+ return Err(err.into());
+ }
+ Ok(())
+ })
+ .on_window_event(|window, event| {
+ if let WindowEvent::CloseRequested { .. } = event {
+ server::stop(window.app_handle());
+ }
+ })
+ .build(tauri::generate_context!())
+ .expect("failed to build CodeDelta desktop")
+ .run(|app, event| {
+ if let RunEvent::Exit = event {
+ server::stop(app);
+ }
+ });
+}
diff --git a/apps/desktop/src-tauri/src/main.rs b/apps/desktop/src-tauri/src/main.rs
new file mode 100644
index 000000000..556d8954c
--- /dev/null
+++ b/apps/desktop/src-tauri/src/main.rs
@@ -0,0 +1,6 @@
+// Prevents additional console window on Windows in release, unused on macOS.
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+fn main() {
+ codedelta_desktop_lib::run();
+}
diff --git a/apps/desktop/src-tauri/src/server.rs b/apps/desktop/src-tauri/src/server.rs
new file mode 100644
index 000000000..16d8053a2
--- /dev/null
+++ b/apps/desktop/src-tauri/src/server.rs
@@ -0,0 +1,221 @@
+use std::path::PathBuf;
+use std::process::{Child, Command, Stdio};
+use std::sync::Mutex;
+use std::time::Duration;
+
+use tauri::{AppHandle, Manager};
+
+static SERVER_CHILD: Mutex> = Mutex::new(None);
+
+const API_PORT: u16 = 3847;
+
+pub fn api_base_url() -> String {
+ format!("http://127.0.0.1:{API_PORT}")
+}
+
+fn health_ok() -> bool {
+ #[cfg(windows)]
+ {
+ let script = format!(
+ "try {{ (Invoke-WebRequest -Uri '{}/api/health' -UseBasicParsing -TimeoutSec 2).StatusCode -eq 200 }} catch {{ $false }}",
+ api_base_url()
+ );
+ let output = Command::new("powershell")
+ .args(["-NoProfile", "-NonInteractive", "-Command", &script])
+ .output();
+ return matches!(output, Ok(o) if o.status.success() && String::from_utf8_lossy(&o.stdout).trim() == "True");
+ }
+
+ #[cfg(not(windows))]
+ {
+ let output = Command::new("curl")
+ .args(["-sf", &format!("{}/api/health", api_base_url())])
+ .output();
+ matches!(output, Ok(o) if o.status.success())
+ }
+}
+
+fn cache_dir() -> Result {
+ let base = dirs::data_dir()
+ .or_else(dirs::home_dir)
+ .ok_or("Could not resolve application data directory")?;
+ Ok(base.join("CodeDelta"))
+}
+
+fn bundled_node_path(runtime: &PathBuf) -> PathBuf {
+ #[cfg(windows)]
+ {
+ runtime.join("node/node.exe")
+ }
+ #[cfg(not(windows))]
+ {
+ runtime.join("node/bin/node")
+ }
+}
+
+fn runtime_has_node(runtime: &PathBuf) -> bool {
+ bundled_node_path(runtime).exists()
+}
+
+fn resolve_runtime_dir(app: &AppHandle) -> Result {
+ let resource = app
+ .path()
+ .resource_dir()
+ .map_err(|e| e.to_string())?;
+ let candidates = [
+ resource.join("resources/runtime"),
+ resource.join("runtime"),
+ ];
+ for staged in candidates {
+ if runtime_has_node(&staged) {
+ return Ok(staged);
+ }
+ }
+
+ #[cfg(debug_assertions)]
+ {
+ let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ let dev_staged = manifest.join("resources/runtime");
+ if runtime_has_node(&dev_staged) {
+ return Ok(dev_staged);
+ }
+ }
+
+ Err(format!(
+ "Bundled runtime not found under {} (tried resources/runtime and runtime). Reinstall from a fresh build.",
+ resource.display()
+ ))
+}
+
+fn resolve_server_entry(runtime: &PathBuf) -> Result {
+ let candidates = [
+ runtime.join("app/node_modules/@codedelta/server/dist/index.js"),
+ runtime.join("app/packages/codedelta-server/dist/index.js"),
+ ];
+ for path in candidates {
+ if path.exists() {
+ return Ok(path);
+ }
+ }
+ Err(format!(
+ "Server entry missing under {} (node_modules and packages paths checked).",
+ runtime.display()
+ ))
+}
+
+fn spawn_bundled(app: &AppHandle) -> Result {
+ let runtime = resolve_runtime_dir(app)?;
+ let node = bundled_node_path(&runtime);
+ let server_js = resolve_server_entry(&runtime)?;
+
+ let web_dist = runtime.join("web-dist");
+ let app_root = runtime.join("app");
+ let cache = cache_dir()?;
+ std::fs::create_dir_all(&cache).map_err(|e| e.to_string())?;
+
+ let mut cmd = Command::new(&node);
+ cmd.arg(&server_js)
+ .current_dir(&app_root)
+ .env("CODEDELTA_PORT", API_PORT.to_string())
+ .env("CODEDELTA_STATIC_DIR", &web_dist)
+ .env("CODEDELTA_MONOREPO_ROOT", &app_root)
+ .env("CODEDELTA_CACHE_DIR", &cache)
+ .env("CODEDELTA_DESKTOP", "1")
+ .env("NODE_ENV", "production")
+ .stdout(Stdio::null())
+ .stderr(Stdio::null());
+
+ cmd.spawn().map_err(|e| format!("Failed to spawn API: {e}"))
+}
+
+fn wait_for_health(timeout: Duration) -> bool {
+ let start = std::time::Instant::now();
+ while start.elapsed() < timeout {
+ if health_ok() {
+ return true;
+ }
+ std::thread::sleep(Duration::from_millis(250));
+ }
+ false
+}
+
+pub fn start(app: &AppHandle) -> Result<(), String> {
+ if cfg!(debug_assertions) {
+ if health_ok() {
+ return Ok(());
+ }
+ return Err(
+ "CodeDelta API is not running on port 3847. Start it with `npm run dev:codedelta` or use `npm run dev:desktop` from the repo root.".into(),
+ );
+ }
+
+ // Production must spawn the bundled server. Do not attach to a dev API on :3847
+ // unless it serves the UI (CODEDELTA_STATIC_DIR or CODEDELTA_DEV_UI_URL).
+ if health_ok() {
+ let hint = port_in_use_hint().unwrap_or_default();
+ return Err(format!(
+ "Port {API_PORT} is already in use. Quit `npm run dev:desktop` or any other CodeDelta/dev server, then reopen the app.\n{hint}"
+ ));
+ }
+
+ let mut child = spawn_bundled(app)?;
+ if wait_for_health(Duration::from_secs(45)) {
+ let mut guard = SERVER_CHILD.lock().map_err(|e| e.to_string())?;
+ *guard = Some(child);
+ return Ok(());
+ }
+ let _ = child.kill();
+ Err("CodeDelta API failed to start within 45 seconds.".into())
+}
+
+pub fn stop(_app: &AppHandle) {
+ if let Ok(mut guard) = SERVER_CHILD.lock() {
+ if let Some(mut child) = guard.take() {
+ let _ = child.kill();
+ }
+ }
+}
+
+pub fn port_in_use_hint() -> Option {
+ #[cfg(windows)]
+ {
+ let output = Command::new("netstat")
+ .args(["-ano"])
+ .output()
+ .ok()?;
+ if !output.status.success() {
+ return None;
+ }
+ let text = String::from_utf8_lossy(&output.stdout);
+ let needle = format!(":{API_PORT}");
+ let lines: Vec<&str> = text
+ .lines()
+ .filter(|l| l.contains(&needle))
+ .collect();
+ if lines.is_empty() {
+ return None;
+ }
+ return Some(format!(
+ "Port {API_PORT} is already in use. Quit the other CodeDelta or dev server and try again.\n{}",
+ lines.join("\n")
+ ));
+ }
+
+ #[cfg(not(windows))]
+ {
+ let output = Command::new("lsof")
+ .args(["-i", &format!(":{API_PORT}")])
+ .output()
+ .ok()?;
+ if !output.status.success() {
+ return None;
+ }
+ let text = String::from_utf8_lossy(&output.stdout);
+ if text.trim().is_empty() {
+ return None;
+ }
+ Some(format!(
+ "Port {API_PORT} is already in use. Quit the other CodeDelta or dev server and try again.\n{text}"
+ ))
+ }
+}
diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json
new file mode 100644
index 000000000..0378e377f
--- /dev/null
+++ b/apps/desktop/src-tauri/tauri.conf.json
@@ -0,0 +1,50 @@
+{
+ "$schema": "https://schema.tauri.app/config/2",
+ "productName": "CodeDelta",
+ "version": "0.2.1",
+ "identifier": "com.codedelta.desktop",
+ "build": {
+ "beforeDevCommand": "",
+ "devUrl": "http://localhost:5173",
+ "beforeBuildCommand": "",
+ "frontendDist": "../src-tauri/resources/runtime/web-dist"
+ },
+ "app": {
+ "withGlobalTauri": false,
+ "windows": [
+ {
+ "label": "main",
+ "title": "CodeDelta",
+ "width": 1280,
+ "height": 840,
+ "minWidth": 900,
+ "minHeight": 600,
+ "url": "http://127.0.0.1:3847"
+ }
+ ],
+ "security": {
+ "csp": null
+ }
+ },
+ "bundle": {
+ "active": true,
+ "targets": ["dmg", "nsis"],
+ "icon": [
+ "icons/32x32.png",
+ "icons/128x128.png",
+ "icons/128x128@2x.png",
+ "icons/icon.icns",
+ "icons/icon.ico"
+ ],
+ "resources": ["resources/runtime/"],
+ "macOS": {
+ "minimumSystemVersion": "12.0",
+ "signingIdentity": "-"
+ },
+ "windows": {
+ "nsis": {
+ "installMode": "currentUser"
+ }
+ }
+ }
+}
diff --git a/apps/web/index.html b/apps/web/index.html
new file mode 100644
index 000000000..5bec4d270
--- /dev/null
+++ b/apps/web/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+ CodeDelta
+
+
+
+
+
+
+
+
+
diff --git a/apps/web/package.json b/apps/web/package.json
new file mode 100644
index 000000000..0cbc9a1b2
--- /dev/null
+++ b/apps/web/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "@codedelta/web",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@xyflow/react": "^12.6.0",
+ "mermaid": "^11.15.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-markdown": "^10.1.0",
+ "react-router-dom": "^7.1.1",
+ "rehype-raw": "^7.0.0",
+ "rehype-sanitize": "^6.0.0",
+ "remark-gfm": "^4.0.1"
+ },
+ "devDependencies": {
+ "@types/react": "^19.0.2",
+ "@types/react-dom": "^19.0.2",
+ "@vitejs/plugin-react": "^4.3.4",
+ "typescript": "^5.0.0",
+ "vite": "^6.0.6"
+ },
+ "license": "MIT"
+}
diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx
new file mode 100644
index 000000000..a4307de17
--- /dev/null
+++ b/apps/web/src/App.tsx
@@ -0,0 +1 @@
+export { default } from './components/AppShell';
diff --git a/apps/web/src/api/client.ts b/apps/web/src/api/client.ts
new file mode 100644
index 000000000..94b4c4c66
--- /dev/null
+++ b/apps/web/src/api/client.ts
@@ -0,0 +1,178 @@
+import type {
+ ChangedFile,
+ CodeNode,
+ CommitDetail,
+ CommitInfo,
+ CompareResponse,
+ FileDiffResponse,
+ GraphDiff,
+ ImpactSummary,
+ ImportRepoRequest,
+ ModelProviderConfig,
+ PanoramaEnrichResult,
+ PanoramaGraph,
+ ProviderKind,
+ RepoRef,
+ TraceAnswer,
+ TraceEvidenceItem,
+ WikiAskAnswer,
+ WikiPageContent,
+ WikiStatus,
+ WikiToc,
+} from '../types';
+
+async function request(path: string, init?: RequestInit): Promise {
+ const res = await fetch(path, {
+ headers: { 'Content-Type': 'application/json', ...init?.headers },
+ ...init,
+ });
+ if (!res.ok) {
+ const body = await res.json().catch(() => ({}));
+ throw new Error((body as { error?: string }).error ?? `Request failed: ${res.status}`);
+ }
+ return res.json() as Promise;
+}
+
+export const api = {
+ health: () =>
+ request<{ status: string; product: string; gitAvailable?: boolean }>('/api/health'),
+
+ listRepos: () => request('/api/repos'),
+
+ importRepo: (body: ImportRepoRequest) =>
+ request('/api/repos/import', { method: 'POST', body: JSON.stringify(body) }),
+
+ getRepo: (id: string) => request(`/api/repos/${id}`),
+
+ listBranches: (id: string) => request(`/api/repos/${id}/branches`),
+
+ listCommits: (id: string, branch: string, limit = 50, skip = 0) =>
+ request(
+ `/api/repos/${id}/commits?branch=${encodeURIComponent(branch)}&limit=${limit}&skip=${skip}`,
+ ),
+
+ getCommit: (id: string, hash: string) =>
+ request(`/api/repos/${id}/commits/${hash}`),
+
+ compare: (id: string, base: string, head: string) =>
+ request(
+ `/api/repos/${id}/compare?base=${encodeURIComponent(base)}&head=${encodeURIComponent(head)}`,
+ ),
+
+ getFileDiff: (id: string, base: string, head: string, file: string) =>
+ request(
+ `/api/repos/${id}/diff?base=${encodeURIComponent(base)}&head=${encodeURIComponent(head)}&file=${encodeURIComponent(file)}`,
+ ),
+
+ runTrace: (
+ id: string,
+ body: { question: string; branch?: string; commitLimit?: number; includeDiffEvidence?: boolean },
+ ) =>
+ request(`/api/repos/${id}/trace`, {
+ method: 'POST',
+ body: JSON.stringify(body),
+ }),
+
+ getProvider: () => request('/api/settings/provider'),
+
+ getCodexAuthStatus: () =>
+ request<{
+ configured: boolean;
+ authMode?: string;
+ codexHome: string;
+ authPath: string;
+ message: string;
+ defaultModel?: string;
+ }>('/api/settings/provider/codex-status'),
+
+ setProvider: (config: ModelProviderConfig) =>
+ request('/api/settings/provider', {
+ method: 'PUT',
+ body: JSON.stringify(config),
+ }),
+
+ getPanorama: (
+ id: string,
+ params: {
+ commit?: string;
+ base?: string;
+ head?: string;
+ root?: string;
+ depth?: number;
+ maxNodes?: number;
+ highlight?: 'trace';
+ traceSymbols?: string[];
+ traceEntryPoints?: string[];
+ },
+ ) => {
+ const q = new URLSearchParams();
+ if (params.commit) q.set('commit', params.commit);
+ if (params.base) q.set('base', params.base);
+ if (params.head) q.set('head', params.head);
+ if (params.root) q.set('root', params.root);
+ if (params.depth != null) q.set('depth', String(params.depth));
+ if (params.maxNodes != null) q.set('maxNodes', String(params.maxNodes));
+ if (params.highlight) q.set('highlight', params.highlight);
+ if (params.traceSymbols?.length) q.set('traceSymbols', params.traceSymbols.join(','));
+ if (params.traceEntryPoints?.length) q.set('traceEntryPoints', params.traceEntryPoints.join(','));
+ return request(`/api/repos/${id}/panorama?${q.toString()}`);
+ },
+
+ enrichPanorama: (id: string, body: { commit: string; nodeIds: string[] }) =>
+ request(`/api/repos/${id}/panorama/enrich`, {
+ method: 'POST',
+ body: JSON.stringify(body),
+ }),
+
+ generateWiki: (id: string, commit: string) =>
+ request<{ status: string; jobId?: string }>(
+ `/api/repos/${id}/wiki/generate?commit=${encodeURIComponent(commit)}`,
+ { method: 'POST' },
+ ),
+
+ getWikiStatus: (id: string, commit: string) =>
+ request(`/api/repos/${id}/wiki/status?commit=${encodeURIComponent(commit)}`),
+
+ getWikiToc: (id: string, commit: string) =>
+ request(`/api/repos/${id}/wiki/toc?commit=${encodeURIComponent(commit)}`),
+
+ getWikiPage: (id: string, commit: string, section: string) =>
+ request(
+ `/api/repos/${id}/wiki/page?commit=${encodeURIComponent(commit)}§ion=${encodeURIComponent(section)}`,
+ ),
+
+ askWiki: (
+ id: string,
+ body: {
+ commit: string;
+ question: string;
+ history?: Array<{ role: 'user' | 'assistant'; content: string }>;
+ },
+ ) =>
+ request(`/api/repos/${id}/wiki/ask`, {
+ method: 'POST',
+ body: JSON.stringify(body),
+ }),
+};
+
+export type {
+ RepoRef,
+ CommitInfo,
+ CommitDetail,
+ ChangedFile,
+ CompareResponse,
+ FileDiffResponse,
+ GraphDiff,
+ ImpactSummary,
+ TraceAnswer,
+ TraceEvidenceItem,
+ CodeNode,
+ ModelProviderConfig,
+ ProviderKind,
+ PanoramaGraph,
+ PanoramaEnrichResult,
+ WikiAskAnswer,
+ WikiPageContent,
+ WikiStatus,
+ WikiToc,
+};
diff --git a/apps/web/src/components/AppShell.tsx b/apps/web/src/components/AppShell.tsx
new file mode 100644
index 000000000..7e2619b52
--- /dev/null
+++ b/apps/web/src/components/AppShell.tsx
@@ -0,0 +1,119 @@
+import { NavLink, Outlet, useMatch } from 'react-router-dom';
+import { RepoProvider, useRepo } from '../context/RepoContext';
+
+function repoNavPath(repoId: string | undefined, page: string): string {
+ if (!repoId) return '/import';
+ return `/repos/${repoId}/${page}`;
+}
+
+function ShellInner() {
+ const match = useMatch('/repos/:repoId/*');
+ const repoId = match?.params.repoId;
+ const repo = useRepo();
+
+ return (
+
+
+
+
+
+
+ Repository
+ (isActive ? 'nav-link active' : 'nav-link')}>
+ Import
+
+ {repoId ? (
+ (isActive ? 'nav-link active' : 'nav-link')}
+ >
+ Commit Timeline
+
+ ) : null}
+
+
+
+ Analysis
+ {repoId ? (
+ <>
+ (isActive ? 'nav-link active' : 'nav-link')}
+ >
+ Delta View
+
+ (isActive ? 'nav-link active' : 'nav-link')}
+ >
+ Trace View
+
+ (isActive ? 'nav-link active' : 'nav-link')}
+ >
+ Panorama
+
+ (isActive ? 'nav-link active' : 'nav-link')}
+ >
+ Wiki
+
+ >
+ ) : (
+ <>
+
+ Delta View
+
+
+ Trace View
+
+
+ Panorama
+
+
+ Wiki
+
+ >
+ )}
+
+
+
+ Settings
+ (isActive ? 'nav-link active' : 'nav-link')}
+ >
+ Provider Settings
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default function AppShell() {
+ return (
+
+
+
+ );
+}
diff --git a/apps/web/src/components/PanoramaGraphView.tsx b/apps/web/src/components/PanoramaGraphView.tsx
new file mode 100644
index 000000000..35b70c927
--- /dev/null
+++ b/apps/web/src/components/PanoramaGraphView.tsx
@@ -0,0 +1,514 @@
+import {
+ Background,
+ Controls,
+ Handle,
+ MiniMap,
+ Position,
+ ReactFlow,
+ ReactFlowProvider,
+ type Edge,
+ type Node,
+ type NodeProps,
+} from '@xyflow/react';
+import '@xyflow/react/dist/style.css';
+import { memo, useCallback, useMemo, useRef, useState } from 'react';
+import { Link } from 'react-router-dom';
+import { downloadPanoramaPng, downloadPanoramaSvg } from '../lib/panorama-export';
+import type { PanoramaFocusCrumb } from '../lib/panorama-focus';
+import type { PanoramaGraph, PanoramaNode } from '../types';
+import { Button } from './ui';
+
+export interface PanoramaGraphViewProps {
+ graph: PanoramaGraph | null;
+ loading?: boolean;
+ error?: string | null;
+ onContinueTrace?: (qualifiedName: string) => void;
+ onNodeClick?: (node: PanoramaNode) => void;
+ onEnrich?: (nodeIds: string[]) => void;
+ enriching?: boolean;
+ impactedEntryPoints?: string[];
+ onFocusEntry?: (qualifiedName: string) => void;
+ /** When true, show delta change legend (Delta View graph tab). */
+ showDeltaLegend?: boolean;
+ /** Clickable focus trail (overview → … → current symbol). */
+ focusTrail?: PanoramaFocusCrumb[];
+ onFocusTrailSelect?: (index: number) => void;
+ canGoBack?: boolean;
+ canGoToOverview?: boolean;
+ onGoBack?: () => void;
+ onGoToOverview?: () => void;
+ /** Optional link to open the same commit in full Panorama page (Delta graph tab). */
+ fullPanoramaHref?: string;
+}
+
+type PanoramaFlowData = {
+ panorama: PanoramaNode;
+ onContinue?: () => void;
+};
+
+function fileBaseName(filePath: string): string {
+ const parts = filePath.split('/');
+ return parts[parts.length - 1] ?? filePath;
+}
+
+function kindLabel(kind: string): string {
+ switch (kind) {
+ case 'route':
+ return 'Route / entry';
+ case 'component':
+ return 'Component';
+ case 'function':
+ return 'Function';
+ case 'method':
+ return 'Method';
+ case 'class':
+ return 'Class';
+ default:
+ return kind.replace(/_/g, ' ');
+ }
+}
+
+function roleLabel(role?: PanoramaNode['role']): string | null {
+ switch (role) {
+ case 'entry':
+ return 'Entry point';
+ case 'bridge':
+ return 'In call chain';
+ case 'leaf':
+ return 'Leaf callee';
+ default:
+ return null;
+ }
+}
+
+function kindAccentClass(kind: string): string {
+ if (kind === 'route') return 'panorama-node--kind-route';
+ if (kind === 'component') return 'panorama-node--kind-component';
+ if (kind === 'function' || kind === 'method') return 'panorama-node--kind-callable';
+ if (kind === 'class') return 'panorama-node--kind-class';
+ return 'panorama-node--kind-default';
+}
+
+function deltaClass(status?: PanoramaNode['deltaStatus']): string {
+ if (!status || status === 'unchanged') return '';
+ return `panorama-node--delta-${status}`;
+}
+
+const BREADCRUMB_MAX = 48;
+
+function truncateCrumbLabel(label: string): string {
+ if (label.length <= BREADCRUMB_MAX) return label;
+ return `${label.slice(0, BREADCRUMB_MAX - 1)}…`;
+}
+
+function PanoramaFocusBreadcrumb({
+ trail,
+ onSelect,
+}: {
+ trail: PanoramaFocusCrumb[];
+ onSelect?: (index: number) => void;
+}) {
+ if (trail.length <= 1) return null;
+
+ return (
+
+
+ {trail.map((crumb, index) => {
+ const isLast = index === trail.length - 1;
+ return (
+
+ {index > 0 && › }
+ {isLast || !onSelect ? (
+
+ {truncateCrumbLabel(crumb.label)}
+
+ ) : (
+ onSelect(index)}
+ >
+ {truncateCrumbLabel(crumb.label)}
+
+ )}
+
+ );
+ })}
+
+
+ );
+}
+
+const PanoramaFlowNode = memo(function PanoramaFlowNode({ data }: NodeProps>) {
+ const n = data.panorama;
+ const fileName = fileBaseName(n.filePath);
+ const lineRange =
+ n.startLine === n.endLine ? `L${n.startLine}` : `L${n.startLine}–L${n.endLine}`;
+ const role = roleLabel(n.role);
+ const commitLabel = n.commitShortHash ?? '—';
+ const signaturePreview = n.signature?.split('\n')[0]?.trim();
+
+ return (
+
+
+
+
+ {kindLabel(n.kind)}
+ {role && {role} }
+ {n.deltaStatus && n.deltaStatus !== 'unchanged' && (
+
+ {n.deltaStatus}
+
+ )}
+
+
{n.name}
+
+
+ File
+ {fileName}
+
+
+ Lines
+ {lineRange}
+
+
+ Commit
+ {commitLabel}
+
+
+
+ {n.filePath}
+
+ {signaturePreview && (
+
{signaturePreview}
+ )}
+ {n.llmLabel &&
{n.llmLabel}
}
+ {data.onContinue && (
+
+ Expand from here
+
+ )}
+
+
+ );
+});
+
+const nodeTypes = { panorama: PanoramaFlowNode };
+
+function edgeLabel(kind: string, synthesizedBy?: string): string {
+ if (synthesizedBy) return `${kind} · ${synthesizedBy}`;
+ return kind;
+}
+
+function toFlowGraph(
+ graph: PanoramaGraph,
+ onContinue?: (qualifiedName: string) => void,
+): { nodes: Node[]; edges: Edge[] } {
+ const nodes: Node[] = graph.nodes.map((n) => ({
+ id: n.id,
+ type: 'panorama',
+ position: n.position ?? { x: 0, y: 0 },
+ data: {
+ panorama: n,
+ onContinue: onContinue ? () => onContinue(n.qualifiedName) : undefined,
+ },
+ }));
+
+ const edges: Edge[] = graph.edges.map((e) => {
+ const isHeuristic = e.provenance === 'heuristic' || Boolean(e.synthesizedBy);
+ const stroke = e.pathHighlight ? '#0969da' : isHeuristic ? '#bf8700' : '#656d76';
+ return {
+ id: e.id,
+ source: e.source,
+ target: e.target,
+ label: edgeLabel(e.kind, e.synthesizedBy),
+ labelStyle: { fill: '#656d76', fontSize: 11, fontWeight: 500 },
+ labelBgStyle: { fill: '#ffffff', fillOpacity: 0.95 },
+ labelBgPadding: [6, 4] as [number, number],
+ labelBgBorderRadius: 4,
+ animated: Boolean(e.pathHighlight),
+ className: [
+ isHeuristic ? 'panorama-edge--heuristic' : '',
+ e.deltaStatus && e.deltaStatus !== 'unchanged' ? `panorama-edge--delta-${e.deltaStatus}` : '',
+ e.pathHighlight ? 'panorama-edge--path' : '',
+ ]
+ .filter(Boolean)
+ .join(' '),
+ style: {
+ stroke,
+ strokeWidth: e.pathHighlight ? 2.5 : 1.5,
+ },
+ };
+ });
+
+ return { nodes, edges };
+}
+
+function PanoramaFlowInner({
+ graph,
+ onContinueTrace,
+ onNodeClick,
+}: {
+ graph: PanoramaGraph;
+ onContinueTrace?: (qualifiedName: string) => void;
+ onNodeClick?: (node: PanoramaNode) => void;
+}) {
+ const wrapperRef = useRef(null);
+ const flow = useMemo(
+ () => toFlowGraph(graph, onContinueTrace),
+ [graph, onContinueTrace],
+ );
+
+ const handleNodeClick = useCallback(
+ (_: React.MouseEvent, node: Node) => {
+ onNodeClick?.(node.data.panorama);
+ },
+ [onNodeClick],
+ );
+
+ return (
+
+
+ '#d0d7de'}
+ maskColor="rgba(240, 243, 246, 0.75)"
+ />
+
+
+
+
+ );
+}
+
+export default function PanoramaGraphView({
+ graph,
+ loading,
+ error,
+ onContinueTrace,
+ onNodeClick,
+ onEnrich,
+ enriching,
+ impactedEntryPoints,
+ onFocusEntry,
+ showDeltaLegend = false,
+ focusTrail,
+ onFocusTrailSelect,
+ canGoBack,
+ canGoToOverview,
+ onGoBack,
+ onGoToOverview,
+ fullPanoramaHref,
+}: PanoramaGraphViewProps) {
+ const [exporting, setExporting] = useState<'svg' | 'png' | null>(null);
+ const [exportError, setExportError] = useState(null);
+
+ const exportSvg = useCallback(() => {
+ if (!graph) return;
+ setExportError(null);
+ setExporting('svg');
+ try {
+ downloadPanoramaSvg(graph);
+ } catch {
+ setExportError('SVG export failed.');
+ } finally {
+ setExporting(null);
+ }
+ }, [graph]);
+
+ const exportPng = useCallback(async () => {
+ if (!graph) return;
+ setExportError(null);
+ setExporting('png');
+ try {
+ await downloadPanoramaPng(graph);
+ } catch {
+ setExportError('PNG export failed — try Export SVG instead.');
+ } finally {
+ setExporting(null);
+ }
+ }, [graph]);
+
+ const hasDelta = graph?.nodes.some((n) => n.deltaStatus && n.deltaStatus !== 'unchanged');
+
+ const sparseOverview =
+ graph &&
+ !canGoToOverview &&
+ (graph.stats.snapshotNodeCount ?? 0) >= 300 &&
+ graph.stats.nodeCount <= Math.max((graph.stats.entrySurfaceCount ?? 6) * 3, 24);
+
+ const showEntryCatalog =
+ graph?.entryCatalog &&
+ graph.entryCatalog.length > 0 &&
+ !canGoToOverview &&
+ onFocusEntry &&
+ (sparseOverview || graph.entryCatalog.length > 6);
+
+ if (loading) {
+ return Loading panorama…
;
+ }
+ if (error) {
+ return {error}
;
+ }
+ if (!graph) {
+ return (
+
+ Select a branch and commit to load the call-flow graph.
+
+ );
+ }
+
+ return (
+
+
+
+
+ {graph.commitShortHash ? `Commit ${graph.commitShortHash}` : 'Call-flow graph'}
+
+ {focusTrail && focusTrail.length > 1 && (
+
+ )}
+
+ {graph.stats.nodeCount} symbols · {graph.stats.edgeCount} edges · depth-limited tree
+ {graph.stats.snapshotNodeCount
+ ? ` · ${graph.stats.snapshotNodeCount.toLocaleString()} indexed`
+ : ''}
+ {graph.stats.truncated ? ' · truncated — expand a node to go deeper' : ''}
+ {graph.stats.effectiveDepth && graph.stats.effectiveDepth > 3
+ ? ` · depth ${graph.stats.effectiveDepth} (large repo boost)`
+ : ''}
+
+ {sparseOverview && (
+
+ Overview shows top entry routes and components — pick an entry below or click{' '}
+ Expand from here on a route or mount point to open its call tree.
+
+ )}
+
+ {graph.extractionMethod === 'fallback' && (
+
Fallback extractor — call edges may be sparse
+ )}
+ {graph.pathMessage &&
{graph.pathMessage} }
+
+ {canGoBack && onGoBack && (
+
+ ← Back
+
+ )}
+ {canGoToOverview && onGoToOverview && (
+
+ All entry points
+
+ )}
+ {fullPanoramaHref && (
+
+ Open full Panorama
+
+ )}
+ {onEnrich && graph.nodes.length > 0 && (
+ onEnrich(graph.nodes.slice(0, 20).map((n) => n.id))}
+ >
+ {enriching ? 'Generating labels…' : 'Generate labels (LLM)'}
+
+ )}
+ {graph.nodes.length > 0 && (
+ <>
+
+ {exporting === 'svg' ? 'Exporting…' : 'Export SVG'}
+
+
+ {exporting === 'png' ? 'Exporting…' : 'Export PNG'}
+
+ >
+ )}
+
+
+ {exportError &&
{exportError}
}
+
+ {showEntryCatalog && (
+
+
Entry surfaces ({graph!.entryCatalog!.length})
+
+ Jump into a route, component, or exported handler.
+
+
+ {graph!.entryCatalog!.map((entry) => (
+
+ onFocusEntry!(entry.qualifiedName)}>
+ {entry.kind}
+ {entry.qualifiedName}
+
+
+ ))}
+
+
+ )}
+
+ {impactedEntryPoints && impactedEntryPoints.length > 0 && onFocusEntry && (
+
+
Impacted entry points
+
+ {impactedEntryPoints.map((ep) => (
+
+ onFocusEntry(ep)}>
+ {ep}
+
+
+ ))}
+
+
+ )}
+
+ {graph.nodes.length === 0 ? (
+
+ No symbols in this subgraph. Try another focus symbol or increase call depth.
+
+ ) : (
+
+
+
+ )}
+
+ {(showDeltaLegend || hasDelta) && (
+
+ Added
+ Modified
+ Removed
+ Heuristic edge
+
+ )}
+
+ );
+}
diff --git a/apps/web/src/components/ServerBootGate.tsx b/apps/web/src/components/ServerBootGate.tsx
new file mode 100644
index 000000000..5dccc178f
--- /dev/null
+++ b/apps/web/src/components/ServerBootGate.tsx
@@ -0,0 +1,106 @@
+import { useCallback, useEffect, useState, type ReactNode } from 'react';
+import { api } from '../api/client';
+import { Alert, Button } from './ui';
+
+type BootState = 'waiting' | 'ready' | 'failed';
+
+const MAX_ATTEMPTS = 24;
+const INITIAL_DELAY_MS = 200;
+
+export function ServerBootGate({ children }: { children: ReactNode }) {
+ const [state, setState] = useState('waiting');
+ const [gitAvailable, setGitAvailable] = useState(true);
+ const [error, setError] = useState(null);
+ const [attempt, setAttempt] = useState(0);
+
+ const poll = useCallback(async () => {
+ try {
+ const health = await api.health();
+ setGitAvailable(health.gitAvailable !== false);
+ setState('ready');
+ setError(null);
+ return true;
+ } catch {
+ return false;
+ }
+ }, []);
+
+ useEffect(() => {
+ if (state !== 'waiting') return;
+
+ let cancelled = false;
+ let timer: ReturnType | undefined;
+
+ async function run(tryIndex: number) {
+ const ok = await poll();
+ if (cancelled) return;
+ if (ok) return;
+ if (tryIndex >= MAX_ATTEMPTS) {
+ setState('failed');
+ setError(
+ 'Cannot reach the CodeDelta API. If you use the desktop app, quit and reopen it. Otherwise run npm run dev:codedelta.',
+ );
+ return;
+ }
+ setAttempt(tryIndex + 1);
+ const delay = Math.min(INITIAL_DELAY_MS * 1.4 ** tryIndex, 3000);
+ timer = setTimeout(() => run(tryIndex + 1), delay);
+ }
+
+ void run(0);
+ return () => {
+ cancelled = true;
+ if (timer) clearTimeout(timer);
+ };
+ }, [poll, state]);
+
+ async function retry() {
+ setState('waiting');
+ setAttempt(0);
+ setError(null);
+ const ok = await poll();
+ if (!ok) {
+ setState('failed');
+ setError('Still cannot reach the API. Check that port 3847 is free.');
+ }
+ }
+
+ if (state === 'waiting') {
+ return (
+
+
+
CodeDelta
+
Starting local services…
+
+ {attempt > 0 ? `Waiting for API (attempt ${attempt})` : 'Connecting to API'}
+
+
+
+ );
+ }
+
+ if (state === 'failed') {
+ return (
+
+
+
CodeDelta
+ {error &&
{error} }
+
void retry()}>
+ Retry
+
+
+
+ );
+ }
+
+ return (
+ <>
+ {!gitAvailable && (
+
+ Git is not available on PATH. Import and compare require git to be installed.
+
+ )}
+ {children}
+ >
+ );
+}
diff --git a/apps/web/src/components/WikiMarkdown.tsx b/apps/web/src/components/WikiMarkdown.tsx
new file mode 100644
index 000000000..db9d7177f
--- /dev/null
+++ b/apps/web/src/components/WikiMarkdown.tsx
@@ -0,0 +1,74 @@
+import { useEffect, useState } from 'react';
+import ReactMarkdown from 'react-markdown';
+import remarkGfm from 'remark-gfm';
+import rehypeRaw from 'rehype-raw';
+import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
+import type { Schema } from 'hast-util-sanitize';
+
+/** Allow common README HTML (headings, images, links) while stripping scripts/handlers. */
+const wikiSanitizeSchema: Schema = {
+ ...defaultSchema,
+ attributes: {
+ ...defaultSchema.attributes,
+ h1: [...(defaultSchema.attributes?.h1 ?? []), 'align'],
+ h2: [...(defaultSchema.attributes?.h2 ?? []), 'align'],
+ p: [...(defaultSchema.attributes?.p ?? []), 'align'],
+ img: [...(defaultSchema.attributes?.img ?? []), 'src', 'alt', 'width', 'height', 'title', 'align'],
+ a: [...(defaultSchema.attributes?.a ?? []), 'href', 'title', 'target', 'rel'],
+ },
+};
+
+let mermaidRenderCounter = 0;
+
+function MermaidBlock({ code }: { code: string }) {
+ const [svg, setSvg] = useState(null);
+ const [failed, setFailed] = useState(false);
+
+ useEffect(() => {
+ let cancelled = false;
+ (async () => {
+ try {
+ const mermaid = (await import('mermaid')).default;
+ mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', theme: 'neutral' });
+ const id = `wiki-mermaid-${mermaidRenderCounter++}`;
+ const { svg: rendered } = await mermaid.render(id, code);
+ if (!cancelled) setSvg(rendered);
+ } catch {
+ if (!cancelled) setFailed(true);
+ }
+ })();
+ return () => {
+ cancelled = true;
+ };
+ }, [code]);
+
+ if (failed) return {code} ;
+ if (!svg) return Rendering diagram…
;
+ return
;
+}
+
+export default function WikiMarkdown({ markdown }: { markdown: string }) {
+ return (
+
+ ;
+ }
+ return (
+
+ {children}
+
+ );
+ },
+ }}
+ >
+ {markdown}
+
+
+ );
+}
diff --git a/apps/web/src/components/index.ts b/apps/web/src/components/index.ts
new file mode 100644
index 000000000..a7440723c
--- /dev/null
+++ b/apps/web/src/components/index.ts
@@ -0,0 +1,2 @@
+export { default as AppShell } from './AppShell';
+export * from './ui';
diff --git a/apps/web/src/components/ui.tsx b/apps/web/src/components/ui.tsx
new file mode 100644
index 000000000..0f9cad526
--- /dev/null
+++ b/apps/web/src/components/ui.tsx
@@ -0,0 +1,181 @@
+import type { ButtonHTMLAttributes, HTMLAttributes, InputHTMLAttributes, ReactNode, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
+
+type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'link';
+type ButtonSize = 'md' | 'sm';
+
+export function Button({
+ variant = 'primary',
+ size = 'md',
+ className = '',
+ children,
+ ...props
+}: ButtonHTMLAttributes & { variant?: ButtonVariant; size?: ButtonSize }) {
+ const v =
+ variant === 'link' ? 'btn-link' : `btn btn-${variant} btn-${size}`;
+ return (
+
+ {children}
+
+ );
+}
+
+export function Card({
+ children,
+ className = '',
+ muted,
+ ...props
+}: HTMLAttributes & { muted?: boolean }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export function CardHeader({ title, description }: { title: string; description?: string }) {
+ return (
+
+
{title}
+ {description &&
{description}
}
+
+ );
+}
+
+type BadgeVariant =
+ | 'default'
+ | 'accent'
+ | 'success'
+ | 'warning'
+ | 'danger'
+ | 'impact-low'
+ | 'impact-medium'
+ | 'impact-high'
+ | 'impact-critical';
+
+export function Badge({
+ children,
+ variant = 'default',
+ className = '',
+}: {
+ children: ReactNode;
+ variant?: BadgeVariant;
+ className?: string;
+}) {
+ const cls = variant === 'default' ? 'badge' : `badge badge-${variant}`;
+ return {children} ;
+}
+
+export function Alert({
+ children,
+ variant = 'error',
+ title,
+}: {
+ children: ReactNode;
+ variant?: 'error' | 'success' | 'warning';
+ title?: string;
+}) {
+ return (
+
+ {title &&
{title} }
+ {title ?
{children}
: children}
+
+ );
+}
+
+export function FormField({
+ label,
+ hint,
+ htmlFor,
+ children,
+}: {
+ label: string;
+ hint?: string;
+ htmlFor?: string;
+ children: ReactNode;
+}) {
+ return (
+
+
{label}
+ {children}
+ {hint &&
{hint}
}
+
+ );
+}
+
+export function PageHeader({
+ title,
+ description,
+ actions,
+}: {
+ title: string;
+ description?: string;
+ actions?: ReactNode;
+}) {
+ return (
+
+
+
+
{title}
+ {description &&
{description}
}
+
+ {actions}
+
+
+ );
+}
+
+export function EmptyState({ title, description }: { title: string; description?: string }) {
+ return (
+
+
+ {title}
+
+ {description &&
{description}
}
+
+ );
+}
+
+export function Mono({ children }: { children: ReactNode }) {
+ return {children};
+}
+
+export function TextInput(props: InputHTMLAttributes) {
+ return ;
+}
+
+export function Select(props: SelectHTMLAttributes) {
+ return ;
+}
+
+export function TextArea(props: TextareaHTMLAttributes) {
+ return ;
+}
+
+export function SelectableCard({
+ selected,
+ onSelect,
+ title,
+ description,
+ disabled,
+}: {
+ selected: boolean;
+ onSelect: () => void;
+ title: string;
+ description: string;
+ disabled?: boolean;
+}) {
+ return (
+
+
+
+
{title}
+
+ {description}
+
+
+
+ );
+}
diff --git a/apps/web/src/context/RepoContext.tsx b/apps/web/src/context/RepoContext.tsx
new file mode 100644
index 000000000..77a375005
--- /dev/null
+++ b/apps/web/src/context/RepoContext.tsx
@@ -0,0 +1,36 @@
+import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
+import { useMatch } from 'react-router-dom';
+import { api, type RepoRef } from '../api/client';
+
+const RepoContext = createContext(null);
+
+export function RepoProvider({ children }: { children: ReactNode }) {
+ const match = useMatch('/repos/:repoId/*');
+ const repoId = match?.params.repoId;
+ const [repo, setRepo] = useState(null);
+
+ useEffect(() => {
+ if (!repoId) {
+ setRepo(null);
+ return;
+ }
+ let cancelled = false;
+ api
+ .getRepo(repoId)
+ .then((r) => {
+ if (!cancelled) setRepo(r);
+ })
+ .catch(() => {
+ if (!cancelled) setRepo(null);
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [repoId]);
+
+ return {children} ;
+}
+
+export function useRepo(): RepoRef | null {
+ return useContext(RepoContext);
+}
diff --git a/apps/web/src/lib/panorama-export.ts b/apps/web/src/lib/panorama-export.ts
new file mode 100644
index 000000000..98a58d6a2
--- /dev/null
+++ b/apps/web/src/lib/panorama-export.ts
@@ -0,0 +1,343 @@
+import type { PanoramaGraph, PanoramaNode } from '../types';
+
+const EXPORT_BG = '#f6f8fa';
+const BASE_NODE_W = 360;
+const BASE_NODE_H = 188;
+const BASE_PADDING = 48;
+/** Target effective pixel density for PNG (SVG logical units × this ≈ output px). */
+const PNG_TARGET_SCALE = 6;
+const PNG_MAX_DIMENSION = 14_000;
+
+const ACCENT: Record = {
+ route: '#fb8500',
+ component: '#0969da',
+ callable: '#1a7f37',
+ class: '#8250df',
+ default: '#8c959f',
+};
+
+const DELTA_STROKE: Record = {
+ added: '#1a7f37',
+ modified: '#9a6700',
+ removed: '#cf222e',
+};
+
+type ExportLayout = {
+ scale: number;
+ nodeW: number;
+ nodeH: number;
+ pad: number;
+};
+
+function layout(scale = 1): ExportLayout {
+ return {
+ scale,
+ nodeW: BASE_NODE_W * scale,
+ nodeH: BASE_NODE_H * scale,
+ pad: BASE_PADDING * scale,
+ };
+}
+
+function accentForKind(kind: string): string {
+ if (kind === 'route') return ACCENT.route!;
+ if (kind === 'component') return ACCENT.component!;
+ if (kind === 'function' || kind === 'method') return ACCENT.callable!;
+ if (kind === 'class') return ACCENT.class!;
+ return ACCENT.default!;
+}
+
+function kindLabel(kind: string): string {
+ switch (kind) {
+ case 'route':
+ return 'Route / entry';
+ case 'component':
+ return 'Component';
+ case 'function':
+ return 'Function';
+ case 'method':
+ return 'Method';
+ case 'class':
+ return 'Class';
+ default:
+ return kind.replace(/_/g, ' ');
+ }
+}
+
+function escapeXml(text: string): string {
+ return text
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+}
+
+function truncate(text: string, max: number): string {
+ if (text.length <= max) return text;
+ return `${text.slice(0, max - 1)}…`;
+}
+
+function fileBaseName(filePath: string): string {
+ const parts = filePath.split('/');
+ return parts[parts.length - 1] ?? filePath;
+}
+
+function s(value: number, scale: number): number {
+ return value * scale;
+}
+
+function computeBounds(nodes: PanoramaNode[], lay: ExportLayout): {
+ minX: number;
+ minY: number;
+ width: number;
+ height: number;
+} {
+ let minX = Infinity;
+ let minY = Infinity;
+ let maxX = -Infinity;
+ let maxY = -Infinity;
+
+ for (const node of nodes) {
+ const x = node.position?.x ?? 0;
+ const y = node.position?.y ?? 0;
+ minX = Math.min(minX, x);
+ minY = Math.min(minY, y);
+ maxX = Math.max(maxX, x + lay.nodeW / lay.scale);
+ maxY = Math.max(maxY, y + lay.nodeH / lay.scale);
+ }
+
+ if (!Number.isFinite(minX)) {
+ return { minX: 0, minY: 0, width: 800 * lay.scale, height: 600 * lay.scale };
+ }
+
+ const contentW = maxX - minX;
+ const contentH = maxY - minY;
+ return {
+ minX,
+ minY,
+ width: contentW * lay.scale + lay.pad * 2,
+ height: contentH * lay.scale + lay.pad * 2,
+ };
+}
+
+function resolvePngRasterScale(width: number, height: number): number {
+ const maxSide = Math.max(width, height);
+ let scale = PNG_TARGET_SCALE;
+ if (maxSide * scale > PNG_MAX_DIMENSION) {
+ scale = Math.max(2, Math.floor(PNG_MAX_DIMENSION / maxSide));
+ }
+ return scale;
+}
+
+function edgePath(
+ source: PanoramaNode,
+ target: PanoramaNode,
+ offsetX: number,
+ offsetY: number,
+ lay: ExportLayout,
+): string {
+ const nodeW = lay.nodeW;
+ const nodeH = lay.nodeH;
+ const sx = (source.position?.x ?? 0) * lay.scale + nodeW / 2 + offsetX;
+ const sy = (source.position?.y ?? 0) * lay.scale + nodeH + offsetY;
+ const tx = (target.position?.x ?? 0) * lay.scale + nodeW / 2 + offsetX;
+ const ty = (target.position?.y ?? 0) * lay.scale + offsetY;
+ const midY = (sy + ty) / 2;
+ return `M ${sx} ${sy} C ${sx} ${midY}, ${tx} ${midY}, ${tx} ${ty}`;
+}
+
+function nodeStroke(node: PanoramaNode, lay: ExportLayout): { color: string; width: number } {
+ if (node.traceHighlight) return { color: '#0969da', width: s(2, lay.scale) };
+ if (node.deltaStatus && node.deltaStatus !== 'unchanged') {
+ return { color: DELTA_STROKE[node.deltaStatus] ?? '#d0d7de', width: s(2, lay.scale) };
+ }
+ return { color: '#d0d7de', width: s(1, lay.scale) };
+}
+
+function renderNode(node: PanoramaNode, offsetX: number, offsetY: number, lay: ExportLayout): string {
+ const x = (node.position?.x ?? 0) * lay.scale + offsetX;
+ const y = (node.position?.y ?? 0) * lay.scale + offsetY;
+ const accent = accentForKind(node.kind);
+ const fileName = fileBaseName(node.filePath);
+ const lineRange =
+ node.startLine === node.endLine ? `L${node.startLine}` : `L${node.startLine}-${node.endLine}`;
+ const commit = node.commitShortHash ?? '-';
+ const title = escapeXml(truncate(node.name, 52));
+ const kind = escapeXml(kindLabel(node.kind));
+ const path = escapeXml(truncate(node.filePath, 58));
+ const stroke = nodeStroke(node, lay);
+ const rx = s(10, lay.scale);
+
+ const roleBadge =
+ node.role === 'entry'
+ ? `Entry point `
+ : node.role === 'leaf'
+ ? `Leaf callee `
+ : '';
+
+ const llmLine = node.llmLabel
+ ? `${escapeXml(truncate(node.llmLabel, 64))} `
+ : '';
+
+ return `
+
+
+
+ ${kind.toUpperCase()}
+ ${roleBadge}
+ ${title}
+ File
+ ${escapeXml(truncate(fileName, 24))}
+ Lines
+ ${escapeXml(lineRange)}
+ Commit
+ ${escapeXml(commit)}
+
+ ${path}
+ ${llmLine}
+ `;
+}
+
+function renderFooter(graph: PanoramaGraph, height: number, lay: ExportLayout): string {
+ const commit = graph.commitShortHash ? `commit ${graph.commitShortHash}` : '';
+ const stats = `${graph.stats.nodeCount} symbols · ${graph.stats.edgeCount} edges`;
+ const label = escapeXml(['CodeDelta Panorama', commit, stats].filter(Boolean).join(' · '));
+ return `${label} `;
+}
+
+export interface BuildPanoramaSvgOptions {
+ /** Scale layout units (2 = double-size SVG for sharper PNG rasterization). */
+ renderScale?: number;
+}
+
+/** Build a standalone SVG document from panorama graph data (true vector, not a DOM screenshot). */
+export function buildPanoramaSvg(graph: PanoramaGraph, options: BuildPanoramaSvgOptions = {}): string {
+ const lay = layout(options.renderScale ?? 1);
+ const nodes = graph.nodes.filter((n) => n.position);
+ const bounds = computeBounds(nodes, lay);
+ const offsetX = lay.pad - bounds.minX * lay.scale;
+ const offsetY = lay.pad - bounds.minY * lay.scale;
+ const nodesById = new Map(nodes.map((n) => [n.id, n]));
+ const svgW = Math.ceil(bounds.width);
+ const svgH = Math.ceil(bounds.height + s(20, lay.scale));
+
+ const edgeEls = graph.edges
+ .map((edge) => {
+ const source = nodesById.get(edge.source);
+ const target = nodesById.get(edge.target);
+ if (!source || !target) return '';
+ const stroke = edge.pathHighlight ? '#0969da' : edge.synthesizedBy ? '#bf8700' : '#656d76';
+ const dash = edge.synthesizedBy ? ` stroke-dasharray="${s(6, lay.scale)} ${s(4, lay.scale)}"` : '';
+ const d = edgePath(source, target, offsetX, offsetY, lay);
+ const label = edge.synthesizedBy ? `${edge.kind} · ${edge.synthesizedBy}` : edge.kind;
+ const sx = (source.position?.x ?? 0) * lay.scale + lay.nodeW / 2 + offsetX;
+ const sy = (source.position?.y ?? 0) * lay.scale + lay.nodeH + offsetY;
+ const tx = (target.position?.x ?? 0) * lay.scale + lay.nodeW / 2 + offsetX;
+ const ty = (target.position?.y ?? 0) * lay.scale + offsetY;
+ const midX = (sx + tx) / 2;
+ const midY = (sy + ty) / 2;
+ const labelW = s(72, lay.scale);
+ const labelH = s(16, lay.scale);
+ return `
+
+
+ ${escapeXml(truncate(label, 22))} `;
+ })
+ .join('');
+
+ const nodeEls = nodes.map((n) => renderNode(n, offsetX, offsetY, lay)).join('');
+ const footer = renderFooter(graph, svgH, lay);
+
+ const title = graph.commitShortHash
+ ? `CodeDelta Panorama · ${graph.commitShortHash}`
+ : 'CodeDelta Panorama';
+
+ return `
+
+ ${escapeXml(title)}
+
+
+ ${edgeEls}
+ ${nodeEls}
+ ${footer}
+
+ `;
+}
+
+function downloadBlob(blob: Blob, filename: string) {
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.download = filename;
+ link.href = url;
+ link.click();
+ window.setTimeout(() => URL.revokeObjectURL(url), 4_000);
+}
+
+function exportBasename(graph: PanoramaGraph): string {
+ return graph.commitShortHash ? `codedelta-panorama-${graph.commitShortHash}` : 'codedelta-panorama';
+}
+
+/** Download infinitely scalable vector export. */
+export function downloadPanoramaSvg(graph: PanoramaGraph): void {
+ const svg = buildPanoramaSvg(graph);
+ downloadBlob(new Blob([svg], { type: 'image/svg+xml;charset=utf-8' }), `${exportBasename(graph)}.svg`);
+}
+
+/** Rasterize vector SVG to a high-DPI PNG. */
+export async function downloadPanoramaPng(graph: PanoramaGraph): Promise {
+ const svgRenderScale = 2;
+ const lay = layout(svgRenderScale);
+ const nodes = graph.nodes.filter((n) => n.position);
+ const bounds = computeBounds(nodes, lay);
+ const rasterScale = resolvePngRasterScale(bounds.width, bounds.height);
+
+ const svg = buildPanoramaSvg(graph, { renderScale: svgRenderScale });
+ const url = URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml;charset=utf-8' }));
+
+ try {
+ const img = new Image();
+ img.decoding = 'async';
+
+ await new Promise((resolve, reject) => {
+ img.onload = () => resolve();
+ img.onerror = () => reject(new Error('SVG rasterize failed'));
+ img.src = url;
+ });
+
+ if (img.decode) {
+ await img.decode();
+ }
+
+ const logicalW = bounds.width;
+ const logicalH = bounds.height + s(20, lay.scale);
+ const canvas = document.createElement('canvas');
+ canvas.width = Math.ceil(logicalW * rasterScale);
+ canvas.height = Math.ceil(logicalH * rasterScale);
+
+ const ctx = canvas.getContext('2d');
+ if (!ctx) throw new Error('Canvas unavailable');
+
+ ctx.fillStyle = EXPORT_BG;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingQuality = 'high';
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
+
+ await new Promise((resolve, reject) => {
+ canvas.toBlob(
+ (blob) => {
+ if (!blob) {
+ reject(new Error('PNG encode failed'));
+ return;
+ }
+ downloadBlob(blob, `${exportBasename(graph)}.png`);
+ resolve();
+ },
+ 'image/png',
+ 1,
+ );
+ });
+ } finally {
+ URL.revokeObjectURL(url);
+ }
+}
diff --git a/apps/web/src/lib/panorama-focus.ts b/apps/web/src/lib/panorama-focus.ts
new file mode 100644
index 000000000..111fd8938
--- /dev/null
+++ b/apps/web/src/lib/panorama-focus.ts
@@ -0,0 +1,112 @@
+/** Focus navigation for panorama drill-down (expand / back / overview / breadcrumb). */
+
+export interface PanoramaFocusCrumb {
+ /** Root query value for this level ('' = all entry points). */
+ root: string;
+ /** Display label (symbol name or "All entry points"). */
+ label: string;
+}
+
+const FOCUS_PATH_SEP = '|';
+
+export function pushFocus(stack: string[], currentRoot: string): string[] {
+ return [...stack, currentRoot];
+}
+
+export function popFocus(stack: string[]): { stack: string[]; root: string } | null {
+ if (stack.length === 0) return null;
+ const next = [...stack];
+ const root = next.pop()!;
+ return { stack: next, root };
+}
+
+export function focusLabel(root: string): string {
+ return root.trim() ? root : 'All entry points';
+}
+
+/** Full trail from overview through stack to the current root. */
+export function buildFocusTrail(stack: string[], currentRoot: string): PanoramaFocusCrumb[] {
+ const crumbs: PanoramaFocusCrumb[] = [{ root: '', label: 'All entry points' }];
+
+ for (const r of stack) {
+ if (r.trim()) {
+ crumbs.push({ root: r, label: focusLabel(r) });
+ }
+ }
+
+ if (currentRoot.trim()) {
+ const last = crumbs[crumbs.length - 1];
+ if (last?.root !== currentRoot) {
+ crumbs.push({ root: currentRoot, label: focusLabel(currentRoot) });
+ }
+ }
+
+ return crumbs;
+}
+
+/** Serialize focus trail for URL (?focusPath=a|b|c). */
+export function serializeFocusPath(trail: PanoramaFocusCrumb[]): string | null {
+ const symbols = trail.map((c) => c.root).filter((r) => r.trim());
+ if (symbols.length === 0) return null;
+ return symbols.map((s) => encodeURIComponent(s)).join(FOCUS_PATH_SEP);
+}
+
+/** Parse ?focusPath= back into stack + current root. */
+export function parseFocusPathParam(param: string | null | undefined): { stack: string[]; root: string } {
+ if (!param?.trim()) return { stack: [], root: '' };
+
+ const segments = param
+ .split(FOCUS_PATH_SEP)
+ .map((s) => {
+ try {
+ return decodeURIComponent(s.trim());
+ } catch {
+ return s.trim();
+ }
+ })
+ .filter(Boolean);
+
+ if (segments.length === 0) return { stack: [], root: '' };
+ if (segments.length === 1) {
+ return { stack: [''], root: segments[0]! };
+ }
+
+ const root = segments[segments.length - 1]!;
+ const stack: string[] = [''];
+ for (let i = 0; i < segments.length - 1; i++) {
+ stack.push(segments[i]!);
+ }
+ return { stack, root };
+}
+
+/** Read focus state from URL (?focusPath= preferred, ?root= legacy). */
+export function resolveFocusFromSearchParams(params: URLSearchParams): { stack: string[]; root: string } {
+ const focusPath = params.get('focusPath');
+ if (focusPath) return parseFocusPathParam(focusPath);
+
+ const legacyRoot = params.get('root')?.trim();
+ if (legacyRoot) return { stack: [''], root: legacyRoot };
+
+ return { stack: [], root: '' };
+}
+
+/** Jump to a breadcrumb index (0 = overview). */
+export function focusAtTrailIndex(
+ stack: string[],
+ currentRoot: string,
+ index: number,
+): { stack: string[]; root: string } | null {
+ const trail = buildFocusTrail(stack, currentRoot);
+ if (index < 0 || index >= trail.length) return null;
+
+ const target = trail[index]!;
+ if (index === 0) {
+ return { stack: [], root: '' };
+ }
+
+ const newStack: string[] = [''];
+ for (let j = 1; j < index; j++) {
+ newStack.push(trail[j]!.root);
+ }
+ return { stack: newStack, root: target.root };
+}
diff --git a/apps/web/src/lib/trace-cache.ts b/apps/web/src/lib/trace-cache.ts
new file mode 100644
index 000000000..d0158717b
--- /dev/null
+++ b/apps/web/src/lib/trace-cache.ts
@@ -0,0 +1,51 @@
+import type { TraceAnswer } from '../api/client';
+
+export interface TraceSession {
+ question: string;
+ branch: string;
+ commitLimit: number;
+ includeDiffEvidence: boolean;
+ result: TraceAnswer;
+ savedAt: number;
+}
+
+const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
+
+function storageKey(repoId: string): string {
+ return `codedelta.trace.${repoId}`;
+}
+
+export function loadTraceSession(repoId: string): TraceSession | null {
+ try {
+ const raw = sessionStorage.getItem(storageKey(repoId));
+ if (!raw) return null;
+ const parsed = JSON.parse(raw) as TraceSession;
+ if (!parsed?.result || Date.now() - parsed.savedAt > MAX_AGE_MS) {
+ sessionStorage.removeItem(storageKey(repoId));
+ return null;
+ }
+ return parsed;
+ } catch {
+ return null;
+ }
+}
+
+export function saveTraceSession(
+ repoId: string,
+ session: Omit,
+): void {
+ try {
+ const payload: TraceSession = { ...session, savedAt: Date.now() };
+ sessionStorage.setItem(storageKey(repoId), JSON.stringify(payload));
+ } catch {
+ /* quota or private mode */
+ }
+}
+
+export function clearTraceSession(repoId: string): void {
+ try {
+ sessionStorage.removeItem(storageKey(repoId));
+ } catch {
+ /* ignore */
+ }
+}
diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx
new file mode 100644
index 000000000..708907783
--- /dev/null
+++ b/apps/web/src/main.tsx
@@ -0,0 +1,16 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import { BrowserRouter } from 'react-router-dom';
+import { ServerBootGate } from './components/ServerBootGate';
+import { AppRoutes } from './routes';
+import './styles.css';
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+
+ ,
+);
diff --git a/apps/web/src/pages/DeltaViewPage.tsx b/apps/web/src/pages/DeltaViewPage.tsx
new file mode 100644
index 000000000..e2d0761ee
--- /dev/null
+++ b/apps/web/src/pages/DeltaViewPage.tsx
@@ -0,0 +1,640 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { Link, useParams, useSearchParams } from 'react-router-dom';
+import {
+ api,
+ type ChangedFile,
+ type CompareResponse,
+ type CommitInfo,
+ type FileDiffResponse,
+ type RepoRef,
+} from '../api/client';
+import {
+ Alert,
+ Badge,
+ Button,
+ Card,
+ CardHeader,
+ FormField,
+ PageHeader,
+ Select,
+} from '../components/ui';
+import PanoramaGraphView from '../components/PanoramaGraphView';
+import { buildFocusTrail, focusAtTrailIndex, popFocus, pushFocus, serializeFocusPath } from '../lib/panorama-focus';
+import type { PanoramaGraph } from '../types';
+
+type TabId = 'files' | 'symbols' | 'edges' | 'metrics' | 'graph';
+type ContributorFactor =
+ | 'changedFiles'
+ | 'changedSymbols'
+ | 'changedEdges'
+ | 'affectedNodes'
+ | 'riskTags'
+ | 'entryPoints';
+
+function contributorLabel(factor: ContributorFactor): string {
+ switch (factor) {
+ case 'affectedNodes':
+ return 'Blast radius';
+ case 'entryPoints':
+ return 'Entry surface';
+ case 'changedEdges':
+ return 'Dependency churn';
+ case 'changedSymbols':
+ return 'Symbol churn';
+ case 'riskTags':
+ return 'Risk signals';
+ case 'changedFiles':
+ return 'File spread';
+ default:
+ return factor;
+ }
+}
+
+function impactBadgeVariant(severity: string): 'impact-low' | 'impact-medium' | 'impact-high' | 'impact-critical' {
+ if (severity === 'critical') return 'impact-critical';
+ if (severity === 'high') return 'impact-high';
+ if (severity === 'medium') return 'impact-medium';
+ return 'impact-low';
+}
+
+function priorityClass(priority: 'high' | 'medium' | 'low'): string {
+ return `priority-${priority}`;
+}
+
+function SymbolTable({
+ title,
+ rows,
+ onOpenFile,
+}: {
+ title: string;
+ rows: Array<{ id: string; kind: string; filePath: string; name: string }>;
+ onOpenFile: (filePath: string) => void;
+}) {
+ if (rows.length === 0) return null;
+ return (
+
+
+ {title} ({rows.length})
+
+
+
+
+ Kind
+ Name
+ File
+
+
+
+ {rows.slice(0, 100).map((r) => (
+ onOpenFile(r.filePath)}>
+ {r.kind}
+ {r.name}
+ {r.filePath}
+
+ ))}
+
+
+ {rows.length > 100 && Showing 100 of {rows.length}
}
+ Symbol click opens file-level diff (symbol-to-hunk mapping planned).
+
+ );
+}
+
+function DiffModal({
+ open,
+ data,
+ loading,
+ error,
+ onClose,
+}: {
+ open: boolean;
+ data: FileDiffResponse | null;
+ loading: boolean;
+ error: string | null;
+ onClose: () => void;
+}) {
+ if (!open) return null;
+ return (
+
+
e.stopPropagation()}>
+
+
File diff
+
+ Close
+
+
+ {loading &&
Loading diff…
}
+ {error &&
{error} }
+ {data && !loading && (
+ <>
+
+ {data.file} ({data.status})
+
+
+ {data.patch.split('\n').map((line, idx) => {
+ const cls = line.startsWith('+')
+ ? 'diff-line added'
+ : line.startsWith('-')
+ ? 'diff-line removed'
+ : line.startsWith('@@')
+ ? 'diff-line hunk'
+ : 'diff-line';
+ return (
+
+ {line}
+
+ );
+ })}
+
+ >
+ )}
+
+
+ );
+}
+
+export default function DeltaViewPage() {
+ const { repoId } = useParams<{ repoId: string }>();
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const [repo, setRepo] = useState(null);
+ const [commits, setCommits] = useState([]);
+ const [base, setBase] = useState(searchParams.get('base') ?? '');
+ const [head, setHead] = useState(searchParams.get('head') ?? '');
+ const [result, setResult] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [tab, setTab] = useState('files');
+
+ const [diffOpen, setDiffOpen] = useState(false);
+ const [diffLoading, setDiffLoading] = useState(false);
+ const [diffError, setDiffError] = useState(null);
+ const [diffData, setDiffData] = useState(null);
+
+ const [panorama, setPanorama] = useState(null);
+ const [panoramaLoading, setPanoramaLoading] = useState(false);
+ const [panoramaError, setPanoramaError] = useState(null);
+ const [panoramaRoot, setPanoramaRoot] = useState('');
+ const [panoramaFocusStack, setPanoramaFocusStack] = useState([]);
+
+ useEffect(() => {
+ if (!repoId) return;
+ api
+ .getRepo(repoId)
+ .then((r) => {
+ setRepo(r);
+ return api.listCommits(repoId, r.defaultBranch, 80);
+ })
+ .then(setCommits)
+ .catch(() => {
+ setRepo(null);
+ setCommits([]);
+ });
+ }, [repoId]);
+
+ // URL is the single driver for compares; this ref dedupes so a click +
+ // searchParams effect never issue the same request twice.
+ const lastRequestedKeyRef = useRef(null);
+
+ const executeCompare = useCallback(
+ async (baseHash: string, headHash: string) => {
+ if (!repoId) return;
+ setLoading(true);
+ setError(null);
+ try {
+ const data = await api.compare(repoId, baseHash, headHash);
+ setResult(data);
+ setTab('files');
+ setPanorama(null);
+ setPanoramaRoot('');
+ setPanoramaFocusStack([]);
+ } catch (err) {
+ setResult(null);
+ setError(err instanceof Error ? err.message : 'Compare failed');
+ } finally {
+ setLoading(false);
+ }
+ },
+ [repoId],
+ );
+
+ const runCompare = useCallback(() => {
+ if (!repoId || !base || !head) return;
+ const qBase = searchParams.get('base');
+ const qHead = searchParams.get('head');
+ if (qBase === base && qHead === head) {
+ // URL already matches (e.g. retry after an error): run directly.
+ lastRequestedKeyRef.current = `${base}\u0000${head}`;
+ executeCompare(base, head);
+ } else {
+ setSearchParams({ base, head });
+ }
+ }, [repoId, base, head, searchParams, setSearchParams, executeCompare]);
+
+ useEffect(() => {
+ const qBase = searchParams.get('base');
+ const qHead = searchParams.get('head');
+ if (qBase) setBase(qBase);
+ if (qHead) setHead(qHead);
+ if (repoId && qBase && qHead) {
+ const key = `${qBase}\u0000${qHead}`;
+ if (lastRequestedKeyRef.current !== key) {
+ lastRequestedKeyRef.current = key;
+ executeCompare(qBase, qHead);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [repoId, searchParams.get('base'), searchParams.get('head'), executeCompare]);
+
+ const loadPanorama = useCallback(
+ async (rootOverride?: string) => {
+ if (!repoId || !base || !head) return;
+ setPanoramaLoading(true);
+ setPanoramaError(null);
+ try {
+ const data = await api.getPanorama(repoId, {
+ base,
+ head,
+ depth: 3,
+ maxNodes: 200,
+ root: (rootOverride ?? panoramaRoot) || undefined,
+ });
+ setPanorama(data);
+ } catch (err) {
+ setPanorama(null);
+ setPanoramaError(err instanceof Error ? err.message : 'Panorama failed');
+ } finally {
+ setPanoramaLoading(false);
+ }
+ },
+ [repoId, base, head, panoramaRoot],
+ );
+
+ useEffect(() => {
+ if (tab === 'graph' && result && !panorama && !panoramaLoading) {
+ loadPanorama();
+ }
+ }, [tab, result, panorama, panoramaLoading, loadPanorama]);
+
+ const handlePanoramaExpand = useCallback(
+ (qualifiedName: string) => {
+ setPanoramaFocusStack((prev) => pushFocus(prev, panoramaRoot));
+ setPanoramaRoot(qualifiedName);
+ loadPanorama(qualifiedName);
+ },
+ [panoramaRoot, loadPanorama],
+ );
+
+ const handlePanoramaGoBack = useCallback(() => {
+ const popped = popFocus(panoramaFocusStack);
+ if (!popped) return;
+ setPanoramaFocusStack(popped.stack);
+ setPanoramaRoot(popped.root);
+ loadPanorama(popped.root || undefined);
+ }, [panoramaFocusStack, loadPanorama]);
+
+ const handlePanoramaOverview = useCallback(() => {
+ setPanoramaFocusStack([]);
+ setPanoramaRoot('');
+ loadPanorama('');
+ }, [loadPanorama]);
+
+ const panoramaFocusTrail = useMemo(
+ () => buildFocusTrail(panoramaFocusStack, panoramaRoot),
+ [panoramaFocusStack, panoramaRoot],
+ );
+
+ const fullPanoramaHref = useMemo(() => {
+ if (!repoId || !head) return undefined;
+ const q = new URLSearchParams({ commit: head, from: 'delta' });
+ if (repo?.defaultBranch) q.set('branch', repo.defaultBranch);
+ const path = serializeFocusPath(panoramaFocusTrail);
+ if (path) q.set('focusPath', path);
+ return `/repos/${repoId}/panorama?${q.toString()}`;
+ }, [repoId, head, repo?.defaultBranch, panoramaFocusTrail]);
+
+ const handlePanoramaTrailSelect = useCallback(
+ (index: number) => {
+ const next = focusAtTrailIndex(panoramaFocusStack, panoramaRoot, index);
+ if (!next) return;
+ setPanoramaFocusStack(next.stack);
+ setPanoramaRoot(next.root);
+ loadPanorama(next.root || undefined);
+ },
+ [panoramaFocusStack, panoramaRoot, loadPanorama],
+ );
+
+ async function openFileDiff(filePath: string) {
+ if (!repoId || !base || !head) return;
+ setDiffOpen(true);
+ setDiffLoading(true);
+ setDiffError(null);
+ setDiffData(null);
+ try {
+ const data = await api.getFileDiff(repoId, base, head, filePath);
+ setDiffData(data);
+ } catch (err) {
+ setDiffError(err instanceof Error ? err.message : 'Failed to load file diff');
+ } finally {
+ setDiffLoading(false);
+ }
+ }
+
+ const baseIndex = commits.findIndex((c) => c.hash === base);
+ const headIndex = commits.findIndex((c) => c.hash === head);
+ const baseOptions = commits.filter((_, idx) => headIndex < 0 || idx > headIndex);
+ const headOptions = commits.filter((_, idx) => baseIndex < 0 || idx < baseIndex);
+ const visibleTabs: TabId[] = ['files', 'symbols', 'edges', 'metrics', 'graph'];
+
+ const changedSymbols = useMemo(() => {
+ if (!result) return 0;
+ return (
+ result.graphDiff.summary.symbolsAdded +
+ result.graphDiff.summary.symbolsRemoved +
+ result.graphDiff.summary.symbolsModified
+ );
+ }, [result]);
+
+ const fromTrace = searchParams.get('from') === 'trace';
+ const severity = result?.impact.explanation?.severity ?? 'low';
+
+ return (
+
+ {fromTrace && repoId && (
+
+ ← Back to Trace results
+
+ )}
+
+
+
+ {repo && (
+
+ Repository: {repo.input}
+
+ )}
+
+
+
+ setBase(e.target.value)}>
+ Select commit…
+ {baseOptions.map((c) => (
+
+ {c.shortHash} — {c.message.slice(0, 60)}
+
+ ))}
+
+
+
+ setHead(e.target.value)}>
+ Select commit…
+ {headOptions.map((c) => (
+
+ {c.shortHash} — {c.message.slice(0, 60)}
+
+ ))}
+
+
+
+ {loading ? 'Comparing…' : 'Compare'}
+
+
+
+ {loading && (
+
+ Comparing structure… the first compare of a commit builds its structural snapshot, which can take a
+ minute or two on large repositories. Subsequent compares reuse the cache.
+
+ )}
+
+ {error &&
{error} }
+
+ {result?.deltaSummary && (
+
+ {result.deltaSummary.title}
+
+ {result.deltaSummary.overview.map((line) => (
+ {line}
+ ))}
+
+
+
+
Main areas
+
+ {result.deltaSummary.mainAreas.map((a) => (
+
+ {a.name} — {a.changedSymbols} symbols
+ {a.riskTags.length > 0 && ({a.riskTags.join(', ')}) }
+
+ ))}
+
+
+
+
Risks
+
+ {result.deltaSummary.risks.length === 0 && No risk tags detected }
+ {result.deltaSummary.risks.map((r) => (
+
+ {r.tag} : {r.reason}
+
+ ))}
+
+
+
+
Suggested review order
+
+ {result.deltaSummary.reviewOrder.map((item) => (
+
+ openFileDiff(item.file)}>
+ {item.file}
+ {' '}
+ [{item.priority}]
+ {item.reason}
+
+ ))}
+
+
+
+
+ )}
+
+ {result?.impact && (
+
+
+
+
{result.impact.score}
+
{severity} impact
+
+ {result.impact.explanation?.summary}
+ {result.impact.explanation?.reasons && (
+
+ {result.impact.explanation.reasons.slice(0, 3).map((r) => (
+ {r}
+ ))}
+
+ )}
+ {result.impact.explanation?.topContributors &&
+ result.impact.explanation.topContributors.length > 0 && (
+ <>
+ Main contributors
+
+ {result.impact.explanation.topContributors.slice(0, 4).map((c) => (
+
+ {contributorLabel(c.factor as ContributorFactor)}: {c.value}
+
+ ))}
+
+ >
+ )}
+
+ )}
+
+ {result && (
+
+
+ {visibleTabs.map((t) => (
+ setTab(t)}
+ >
+ {t.charAt(0).toUpperCase() + t.slice(1)}
+
+ ))}
+
+
+ {tab === 'files' && (
+
+ {result.graphDiff.changedFiles.map((f: ChangedFile) => (
+
+ openFileDiff(f.path)}>
+ {f.path}
+ {' '}
+ {f.status}
+
+ ))}
+
+ )}
+
+ {tab === 'symbols' && (
+ <>
+
+
+ {result.graphDiff.modifiedNodes.length > 0 && (
+
+ Modified ({result.graphDiff.modifiedNodes.length})
+
+ {result.graphDiff.modifiedNodes.slice(0, 80).map((m) => (
+
+ openFileDiff(m.after.filePath)}>
+ {m.after.filePath}
+ {' '}
+ — {m.after.name} ({m.changes.join(', ')})
+
+ ))}
+
+
+ )}
+ >
+ )}
+
+ {tab === 'edges' && (
+
+ Edge changes
+
+ +{result.graphDiff.addedEdges.length} / −{result.graphDiff.removedEdges.length}
+
+
+ {result.graphDiff.addedEdges.slice(0, 40).map((e, i) => (
+
+ + {e.kind}: {e.source} → {e.target}
+
+ ))}
+ {result.graphDiff.removedEdges.slice(0, 40).map((e, i) => (
+
+ − {e.kind}: {e.source} → {e.target}
+
+ ))}
+
+
+ )}
+
+ {tab === 'metrics' && (
+
+ Changed files
+ {result.graphDiff.changedFiles.length}
+ Changed symbols
+ {changedSymbols}
+ Edge changes
+ {result.graphDiff.summary.edgesAdded + result.graphDiff.summary.edgesRemoved}
+ Affected nodes
+ {result.graphDiff.affectedNodeIds.length}
+ Base extraction
+
+ {result.baseMeta.extractionMethod} ({result.baseMeta.nodeCount} nodes)
+
+ Head extraction
+
+ {result.headMeta.extractionMethod} ({result.headMeta.nodeCount} nodes)
+
+
+ )}
+
+ {tab === 'graph' && (
+ <>
+
+ Structural diff overlay on the head commit's call tree. For a single-commit map without diff coloring,{' '}
+ {repoId && head ? (
+ open full Panorama
+ ) : (
+ 'open Panorama'
+ )}
+ .
+
+ 0}
+ canGoToOverview={panoramaRoot.trim().length > 0}
+ onGoBack={handlePanoramaGoBack}
+ onGoToOverview={handlePanoramaOverview}
+ fullPanoramaHref={fullPanoramaHref}
+ impactedEntryPoints={result.impact.impactedEntryPoints}
+ onFocusEntry={(ep) => {
+ setPanoramaFocusStack((prev) => pushFocus(prev, panoramaRoot));
+ setPanoramaRoot(ep);
+ loadPanorama(ep);
+ }}
+ onContinueTrace={handlePanoramaExpand}
+ onNodeClick={(node) => openFileDiff(node.filePath)}
+ />
+ >
+ )}
+
+ )}
+
+ {!result && !loading && !error && (
+
+ Select base and head commits, or open from{' '}
+ Commit Timeline.
+
+ )}
+
+
setDiffOpen(false)}
+ />
+
+ );
+}
diff --git a/apps/web/src/pages/ImportPage.tsx b/apps/web/src/pages/ImportPage.tsx
new file mode 100644
index 000000000..bbd30d656
--- /dev/null
+++ b/apps/web/src/pages/ImportPage.tsx
@@ -0,0 +1,116 @@
+import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { api, type RepoRef } from '../api/client';
+import {
+ Alert,
+ Button,
+ Card,
+ CardHeader,
+ FormField,
+ PageHeader,
+ TextInput,
+} from '../components/ui';
+
+export default function ImportPage() {
+ const navigate = useNavigate();
+ const [githubUrl, setGithubUrl] = useState('');
+ const [localPath, setLocalPath] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [recentRepos, setRecentRepos] = useState([]);
+
+ useEffect(() => {
+ api
+ .listRepos()
+ .then(setRecentRepos)
+ .catch(() => setRecentRepos([]));
+ }, []);
+
+ async function handleImport(source: 'github' | 'local', input: string) {
+ if (!input.trim()) return;
+ setLoading(true);
+ setError(null);
+ try {
+ const ref = await api.importRepo({ source, input: input.trim() });
+ navigate(`/repos/${ref.id}/timeline`);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Import failed');
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ return (
+
+
+
+ {error &&
{error} }
+
+ {recentRepos.length > 0 && (
+
+
+
+ {recentRepos.map((repo) => (
+
+ navigate(`/repos/${repo.id}/timeline`)}
+ >
+ {repo.input}
+
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ setGithubUrl(e.target.value)}
+ disabled={loading}
+ />
+
+ handleImport('github', githubUrl)}
+ >
+ {loading ? 'Importing…' : 'Import from GitHub'}
+
+
+
+
+
+
+ setLocalPath(e.target.value)}
+ disabled={loading}
+ />
+
+ handleImport('local', localPath)}
+ >
+ {loading ? 'Opening…' : 'Open local repository'}
+
+
+
+
+ );
+}
diff --git a/apps/web/src/pages/PanoramaPage.tsx b/apps/web/src/pages/PanoramaPage.tsx
new file mode 100644
index 000000000..80ad8bad0
--- /dev/null
+++ b/apps/web/src/pages/PanoramaPage.tsx
@@ -0,0 +1,535 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { Link, useParams, useSearchParams } from 'react-router-dom';
+import { api, type CommitInfo, type PanoramaGraph } from '../api/client';
+import PanoramaGraphView from '../components/PanoramaGraphView';
+import {
+ buildFocusTrail,
+ focusAtTrailIndex,
+ popFocus,
+ pushFocus,
+ resolveFocusFromSearchParams,
+ serializeFocusPath,
+} from '../lib/panorama-focus';
+import { Alert, Button, Card, FormField, PageHeader, Select } from '../components/ui';
+
+function resolveCommitFromParams(
+ searchParams: URLSearchParams,
+ commits: CommitInfo[],
+): string {
+ const direct = searchParams.get('commit')?.trim();
+ if (direct) return direct;
+ const head = searchParams.get('head')?.trim();
+ if (head) return head;
+ return commits[0]?.hash ?? '';
+}
+
+function orphanCommitOption(hash: string): CommitInfo {
+ return {
+ hash,
+ shortHash: hash.slice(0, 7),
+ message: 'Outside current branch history',
+ author: '',
+ authorEmail: '',
+ date: '',
+ parents: [],
+ changedFilesCount: 0,
+ };
+}
+
+function buildPanoramaRequestKey(
+ commit: string,
+ depth: number,
+ root: string,
+ stack: string[],
+ traceMode: boolean,
+ traceSymbols: string | null,
+ traceEntryPoints: string | null,
+): string {
+ return [
+ commit,
+ depth,
+ root,
+ stack.join('>'),
+ traceMode ? 'trace' : '',
+ traceSymbols ?? '',
+ traceEntryPoints ?? '',
+ ].join('|');
+}
+
+export default function PanoramaPage() {
+ const { repoId } = useParams<{ repoId: string }>();
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const [branches, setBranches] = useState([]);
+ const [branch, setBranch] = useState('');
+ const [commits, setCommits] = useState([]);
+ const [commit, setCommit] = useState('');
+ const initialFocus = resolveFocusFromSearchParams(searchParams);
+ const [root, setRoot] = useState(initialFocus.root);
+ const [focusStack, setFocusStack] = useState(initialFocus.stack);
+ const [depth, setDepth] = useState(Number(searchParams.get('depth') ?? '3') || 3);
+ const [graph, setGraph] = useState(null);
+ const [loading, setLoading] = useState(false);
+ const [enriching, setEnriching] = useState(false);
+ const [error, setError] = useState(null);
+
+ const lastLoadedKeyRef = useRef('');
+ const loadGenerationRef = useRef(0);
+ const [refreshToken, setRefreshToken] = useState(0);
+
+ const traceMode = searchParams.get('highlight') === 'trace';
+ const fromTrace = searchParams.get('from') === 'trace';
+ const fromDelta = searchParams.get('from') === 'delta';
+
+ const commitsForSelect = useMemo(() => {
+ if (!commit || commits.some((c) => c.hash === commit)) return commits;
+ return [orphanCommitOption(commit), ...commits];
+ }, [commits, commit]);
+
+ const selectedCommit = useMemo(
+ () => commitsForSelect.find((c) => c.hash === commit),
+ [commitsForSelect, commit],
+ );
+
+ const parentCommit = selectedCommit?.parents[0];
+
+ const syncPanoramaUrl = useCallback(
+ (params: { commit: string; depth: number; branch: string; stack: string[]; root: string }) => {
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ next.set('commit', params.commit);
+ next.delete('base');
+ next.delete('head');
+ next.set('depth', String(params.depth));
+ if (params.branch) next.set('branch', params.branch);
+ else next.delete('branch');
+ next.delete('root');
+ const path = serializeFocusPath(buildFocusTrail(params.stack, params.root));
+ if (path) next.set('focusPath', path);
+ else next.delete('focusPath');
+ return prev.toString() === next.toString() ? prev : next;
+ });
+ },
+ [setSearchParams],
+ );
+
+ const navigateFocus = useCallback(
+ (stack: string[], nextRoot: string) => {
+ lastLoadedKeyRef.current = '';
+ setFocusStack(stack);
+ setRoot(nextRoot);
+ const activeCommit = resolveCommitFromParams(searchParams, commits);
+ if (!activeCommit) return;
+ syncPanoramaUrl({
+ commit: activeCommit,
+ depth: Number(searchParams.get('depth') ?? '3') || 3,
+ branch,
+ stack,
+ root: nextRoot,
+ });
+ },
+ [branch, commits, searchParams, syncPanoramaUrl],
+ );
+
+ useEffect(() => {
+ if (!repoId) return;
+ let cancelled = false;
+
+ (async () => {
+ try {
+ const r = await api.getRepo(repoId);
+ if (cancelled) return;
+
+ const branchList = await api.listBranches(repoId);
+ if (cancelled) return;
+ setBranches(branchList);
+
+ const fromUrl = searchParams.get('branch');
+ const initialBranch =
+ fromUrl && branchList.includes(fromUrl)
+ ? fromUrl
+ : branchList.includes(r.defaultBranch)
+ ? r.defaultBranch
+ : (branchList[0] ?? r.defaultBranch);
+ setBranch(initialBranch);
+ } catch {
+ if (!cancelled) setBranches([]);
+ }
+ })();
+
+ return () => {
+ cancelled = true;
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [repoId]);
+
+ useEffect(() => {
+ if (!repoId || !branch) return;
+ let cancelled = false;
+
+ api
+ .listCommits(repoId, branch, 80)
+ .then((list) => {
+ if (cancelled) return;
+ setCommits(list);
+
+ const urlCommit = searchParams.get('commit')?.trim();
+ const head = searchParams.get('head')?.trim();
+ const direct = urlCommit || head;
+ const resolved =
+ direct && (list.some((c) => c.hash === direct) || urlCommit)
+ ? direct
+ : (list[0]?.hash ?? '');
+
+ if (resolved && resolved !== searchParams.get('commit')) {
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ next.set('commit', resolved);
+ return prev.toString() === next.toString() ? prev : next;
+ });
+ }
+ })
+ .catch(() => {
+ if (!cancelled) setCommits([]);
+ });
+
+ return () => {
+ cancelled = true;
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [repoId, branch]);
+
+ useEffect(() => {
+ const qBranch = searchParams.get('branch');
+ if (qBranch && branches.includes(qBranch) && qBranch !== branch) {
+ setBranch(qBranch);
+ }
+ }, [searchParams, branches, branch]);
+
+ useEffect(() => {
+ const qDepth = searchParams.get('depth');
+ if (qDepth) {
+ const parsed = Number(qDepth) || 3;
+ if (parsed !== depth) setDepth(parsed);
+ }
+ }, [searchParams, depth]);
+
+ useEffect(() => {
+ const qCommit = resolveCommitFromParams(searchParams, commits);
+ if (!repoId || !branch || commits.length === 0 || !qCommit) return;
+
+ const parsedFocus = resolveFocusFromSearchParams(searchParams);
+ const qDepth = Number(searchParams.get('depth') ?? '3') || 3;
+ const traceSymbols = searchParams.get('traceSymbols');
+ const traceEntryPoints = searchParams.get('traceEntryPoints');
+ const requestKey = buildPanoramaRequestKey(
+ qCommit,
+ qDepth,
+ parsedFocus.root,
+ parsedFocus.stack,
+ traceMode,
+ traceSymbols,
+ traceEntryPoints,
+ );
+
+ if (requestKey === lastLoadedKeyRef.current) return;
+
+ if (qCommit !== commit) setCommit(qCommit);
+ if (parsedFocus.root !== root || parsedFocus.stack.join('>') !== focusStack.join('>')) {
+ setFocusStack(parsedFocus.stack);
+ setRoot(parsedFocus.root);
+ }
+
+ const gen = ++loadGenerationRef.current;
+ let cancelled = false;
+
+ (async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const params: Parameters[1] = {
+ commit: qCommit,
+ depth: qDepth,
+ root: parsedFocus.root.trim() || undefined,
+ };
+ if (traceMode) {
+ params.highlight = 'trace';
+ if (traceSymbols) params.traceSymbols = traceSymbols.split(',').filter(Boolean);
+ if (traceEntryPoints) params.traceEntryPoints = traceEntryPoints.split(',').filter(Boolean);
+ }
+ const data = await api.getPanorama(repoId, params);
+ if (cancelled || gen !== loadGenerationRef.current) return;
+ setGraph(data);
+ lastLoadedKeyRef.current = requestKey;
+ syncPanoramaUrl({
+ commit: qCommit,
+ depth: qDepth,
+ branch,
+ stack: parsedFocus.stack,
+ root: parsedFocus.root,
+ });
+ } catch (err) {
+ if (cancelled || gen !== loadGenerationRef.current) return;
+ setGraph(null);
+ setError(err instanceof Error ? err.message : 'Panorama failed');
+ } finally {
+ if (!cancelled && gen === loadGenerationRef.current) {
+ setLoading(false);
+ }
+ }
+ })();
+
+ return () => {
+ cancelled = true;
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [
+ repoId,
+ branch,
+ commits,
+ traceMode,
+ searchParams.get('commit'),
+ searchParams.get('head'),
+ searchParams.get('depth'),
+ searchParams.get('focusPath'),
+ searchParams.get('traceSymbols'),
+ searchParams.get('traceEntryPoints'),
+ refreshToken,
+ ]);
+
+ const handleBranchChange = useCallback(
+ (nextBranch: string) => {
+ if (!repoId || nextBranch === branch) return;
+ lastLoadedKeyRef.current = '';
+ setBranch(nextBranch);
+ setFocusStack([]);
+ setRoot('');
+ setGraph(null);
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ next.set('branch', nextBranch);
+ next.delete('commit');
+ next.delete('focusPath');
+ return prev.toString() === next.toString() ? prev : next;
+ });
+ },
+ [repoId, branch, setSearchParams],
+ );
+
+ const handleCommitChange = useCallback(
+ (nextCommit: string) => {
+ if (nextCommit === commit) return;
+ lastLoadedKeyRef.current = '';
+ setFocusStack([]);
+ setRoot('');
+ setGraph(null);
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ if (nextCommit) next.set('commit', nextCommit);
+ else next.delete('commit');
+ next.delete('focusPath');
+ return prev.toString() === next.toString() ? prev : next;
+ });
+ },
+ [commit, setSearchParams],
+ );
+
+ const handleDepthChange = useCallback(
+ (nextDepth: number) => {
+ if (nextDepth === depth) return;
+ lastLoadedKeyRef.current = '';
+ setDepth(nextDepth);
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ next.set('depth', String(nextDepth));
+ return prev.toString() === next.toString() ? prev : next;
+ });
+ },
+ [depth, setSearchParams],
+ );
+
+ const handleExpand = useCallback(
+ (qualifiedName: string) => {
+ navigateFocus(pushFocus(focusStack, root), qualifiedName);
+ },
+ [focusStack, root, navigateFocus],
+ );
+
+ const handleGoBack = useCallback(() => {
+ const popped = popFocus(focusStack);
+ if (!popped) return;
+ navigateFocus(popped.stack, popped.root);
+ }, [focusStack, navigateFocus]);
+
+ const handleGoToOverview = useCallback(() => {
+ navigateFocus([], '');
+ }, [navigateFocus]);
+
+ const focusTrail = useMemo(() => buildFocusTrail(focusStack, root), [focusStack, root]);
+
+ const handleFocusTrailSelect = useCallback(
+ (index: number) => {
+ const next = focusAtTrailIndex(focusStack, root, index);
+ if (!next) return;
+ navigateFocus(next.stack, next.root);
+ },
+ [focusStack, root, navigateFocus],
+ );
+
+ const handleFocusEntry = useCallback(
+ (qualifiedName: string) => {
+ navigateFocus([], qualifiedName);
+ },
+ [navigateFocus],
+ );
+
+ async function handleEnrich(nodeIds: string[]) {
+ if (!repoId || !commit) return;
+ setEnriching(true);
+ try {
+ const { labels } = await api.enrichPanorama(repoId, { commit, nodeIds });
+ setGraph((prev) => {
+ if (!prev) return prev;
+ return {
+ ...prev,
+ nodes: prev.nodes.map((n) => ({
+ ...n,
+ llmLabel: labels[n.id] ?? n.llmLabel,
+ })),
+ };
+ });
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Enrich failed');
+ } finally {
+ setEnriching(false);
+ }
+ }
+
+ const deltaHref =
+ repoId && commit
+ ? `/repos/${repoId}/delta?head=${commit}${parentCommit ? `&base=${parentCommit}` : ''}&from=panorama`
+ : null;
+
+ return (
+
+ {fromTrace && repoId && (
+
+ ← Back to Trace results
+
+ )}
+ {fromDelta && repoId && (
+
+ ← Back to Delta View
+
+ )}
+
+
+
+
+ Call-flow graph at one commit. Drill down with Expand from here ; the URL keeps your path.{' '}
+ {deltaHref && (
+ <>
+ Delta View colors changes;{' '}
+ >
+ )}
+ {repoId && Trace} opens here from a candidate commit.
+
+
+
+
+
+ handleBranchChange(e.target.value)} disabled={!branch}>
+ {!branch && Loading… }
+ {branches.map((b) => (
+
+ {b}
+
+ ))}
+
+
+
+
+ handleCommitChange(e.target.value)}>
+ Select commit…
+ {commitsForSelect.map((c) => (
+
+ {c.shortHash} — {c.message.slice(0, 55)}
+
+ ))}
+
+
+
+
+ handleDepthChange(Number(e.target.value))}>
+ 2 hops
+ 3 hops
+ 4 hops
+ 5 hops
+
+
+
+
+ setRoot(e.target.value)}
+ placeholder="e.g. GET /api/users or MainActivity.onCreate"
+ />
+
+
+ {
+ if (root.trim()) {
+ navigateFocus([], root.trim());
+ } else {
+ lastLoadedKeyRef.current = '';
+ setRefreshToken((t) => t + 1);
+ }
+ }}
+ disabled={loading || !commit}
+ >
+ {loading ? 'Loading…' : root.trim() ? 'Apply focus' : 'Refresh'}
+
+
+ {selectedCommit && (
+
+ {branch && (
+ <>
+ Branch {branch}
+ {' · '}
+ >
+ )}
+ Snapshot {selectedCommit.shortHash} · {selectedCommit.message.slice(0, 80)}
+ {graph?.stats.effectiveDepth && graph.stats.effectiveDepth > depth && (
+ <>
+ {' · '}
+ Overview uses depth {graph.stats.effectiveDepth} on this repo size
+ >
+ )}
+
+ )}
+
+
+ {error &&
{error} }
+
+
+
0}
+ canGoToOverview={root.trim().length > 0}
+ onGoBack={handleGoBack}
+ onGoToOverview={handleGoToOverview}
+ onContinueTrace={handleExpand}
+ onFocusEntry={handleFocusEntry}
+ onEnrich={handleEnrich}
+ />
+
+
+ );
+}
diff --git a/apps/web/src/pages/ProviderSettingsPage.tsx b/apps/web/src/pages/ProviderSettingsPage.tsx
new file mode 100644
index 000000000..2857fbbda
--- /dev/null
+++ b/apps/web/src/pages/ProviderSettingsPage.tsx
@@ -0,0 +1,179 @@
+import { useEffect, useState } from 'react';
+import { api, type ModelProviderConfig, type ProviderKind } from '../api/client';
+import {
+ Alert,
+ Button,
+ Card,
+ CardHeader,
+ FormField,
+ PageHeader,
+ SelectableCard,
+ TextInput,
+} from '../components/ui';
+
+type CodexAuthStatus = Awaited>;
+
+const PROVIDERS: { kind: ProviderKind; label: string; description: string }[] = [
+ {
+ kind: 'none',
+ label: 'No AI',
+ description: 'Deterministic trace candidates and evidence only; no model calls.',
+ },
+ {
+ kind: 'codex-oauth',
+ label: 'Codex OAuth',
+ description: 'Reuse local Codex CLI login (~/.codex/auth.json). Run codex login first.',
+ },
+ {
+ kind: 'openai',
+ label: 'OpenAI API key',
+ description: 'Direct OpenAI chat/completions API.',
+ },
+ {
+ kind: 'openai-compatible',
+ label: 'OpenAI-compatible',
+ description: 'Custom endpoint with an OpenAI-compatible API.',
+ },
+ { kind: 'anthropic', label: 'Anthropic', description: 'Not implemented yet.' },
+ { kind: 'ollama', label: 'Ollama', description: 'Not implemented yet.' },
+];
+
+const UNIMPLEMENTED: ProviderKind[] = ['anthropic', 'ollama'];
+
+export default function ProviderSettingsPage() {
+ const [config, setConfig] = useState({ kind: 'none' });
+ const [codexStatus, setCodexStatus] = useState(null);
+ const [saved, setSaved] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ api.getProvider().then(setConfig).catch(() => setError('Failed to load settings'));
+ }, []);
+
+ useEffect(() => {
+ api
+ .getCodexAuthStatus()
+ .then(setCodexStatus)
+ .catch(() => setCodexStatus(null));
+ }, [config.kind, saved]);
+
+ async function save() {
+ setError(null);
+ setSaved(false);
+ if (UNIMPLEMENTED.includes(config.kind)) {
+ setError('This provider is not implemented yet. Choose No AI, Codex OAuth, or OpenAI.');
+ return;
+ }
+ if (config.kind === 'codex-oauth' && codexStatus && !codexStatus.configured) {
+ setError(codexStatus.message);
+ return;
+ }
+ try {
+ const updated = await api.setProvider(config);
+ setConfig(updated);
+ setSaved(true);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Save failed');
+ }
+ }
+
+ const showOpenAiFields = config.kind === 'openai' || config.kind === 'openai-compatible';
+ const showCodexFields = config.kind === 'codex-oauth';
+
+ return (
+
+
+
+ {error &&
{error} }
+ {saved &&
Settings saved. }
+
+
+
+ {PROVIDERS.map((p) => (
+
+ setConfig({
+ kind: p.kind,
+ model:
+ p.kind === 'codex-oauth' && codexStatus?.defaultModel
+ ? codexStatus.defaultModel
+ : config.model,
+ })
+ }
+ />
+ ))}
+
+
+ {showCodexFields && (
+
+
+ {codexStatus ? (
+
+
+ {codexStatus.message}
+
+ ) : (
+ Checking local Codex configuration…
+ )}
+ {codexStatus && (
+
+ Config directory: {codexStatus.codexHome}
+
+ )}
+
+ setConfig({ ...config, model: e.target.value || undefined })}
+ />
+
+
+ Run codex login in your terminal; no API key is stored in CodeDelta.
+
+
+ )}
+
+ {showOpenAiFields && (
+
+
+
+ setConfig({ ...config, apiKey: e.target.value })}
+ autoComplete="off"
+ />
+
+ {config.kind === 'openai-compatible' && (
+
+ setConfig({ ...config, baseUrl: e.target.value })}
+ placeholder="https://api.example.com/v1"
+ />
+
+ )}
+
+ setConfig({ ...config, model: e.target.value })}
+ placeholder="gpt-4o-mini"
+ />
+
+
+ )}
+
+
+ Save settings
+
+
+ );
+}
diff --git a/apps/web/src/pages/TimelinePage.tsx b/apps/web/src/pages/TimelinePage.tsx
new file mode 100644
index 000000000..9fa3b9064
--- /dev/null
+++ b/apps/web/src/pages/TimelinePage.tsx
@@ -0,0 +1,210 @@
+import { useCallback, useEffect, useState } from 'react';
+import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
+import { api, type ChangedFile, type CommitDetail, type CommitInfo, type RepoRef } from '../api/client';
+import { Alert, Button, Card, FormField, Mono, PageHeader, Select } from '../components/ui';
+
+export default function TimelinePage() {
+ const { repoId } = useParams<{ repoId: string }>();
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+
+ const [repo, setRepo] = useState(null);
+ const [branches, setBranches] = useState([]);
+ const [branch, setBranch] = useState('');
+ const [commits, setCommits] = useState([]);
+ const [selected, setSelected] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ const loadCommits = useCallback(async (id: string, b: string) => {
+ const list = await api.listCommits(id, b);
+ setCommits(list);
+ }, []);
+
+ useEffect(() => {
+ if (!repoId) return;
+ let cancelled = false;
+
+ async function load() {
+ setLoading(true);
+ setError(null);
+ try {
+ const ref = await api.getRepo(repoId!);
+ if (cancelled) return;
+ setRepo(ref);
+
+ const branchList = await api.listBranches(repoId!);
+ if (cancelled) return;
+ setBranches(branchList);
+
+ const initialBranch = searchParams.get('branch') ?? ref.defaultBranch;
+ setBranch(branchList.includes(initialBranch) ? initialBranch : (branchList[0] ?? ref.defaultBranch));
+
+ await loadCommits(repoId!, initialBranch);
+ } catch (err) {
+ if (!cancelled) {
+ setError(err instanceof Error ? err.message : 'Failed to load repository');
+ }
+ } finally {
+ if (!cancelled) setLoading(false);
+ }
+ }
+
+ load();
+ return () => {
+ cancelled = true;
+ };
+ }, [repoId, searchParams, loadCommits]);
+
+ useEffect(() => {
+ if (!repoId || !branch || loading) return;
+ loadCommits(repoId, branch).catch((err) => {
+ setError(err instanceof Error ? err.message : 'Failed to load commits');
+ });
+ }, [repoId, branch, loading, loadCommits]);
+
+ async function selectCommit(hash: string) {
+ if (!repoId) return;
+ try {
+ const detail = await api.getCommit(repoId, hash);
+ setSelected(detail);
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to load commit');
+ }
+ }
+
+ function openDelta(base: string, head: string) {
+ navigate(`/repos/${repoId}/delta?base=${base}&head=${head}`);
+ }
+
+ function openPanorama(commitHash: string) {
+ navigate(`/repos/${repoId}/panorama?commit=${commitHash}&branch=${encodeURIComponent(branch)}`);
+ }
+
+ function openTrace(commitHash: string) {
+ navigate(`/repos/${repoId}/trace?candidate=${commitHash}`);
+ }
+
+ if (loading) {
+ return (
+
+ );
+ }
+ if (error) {
+ return (
+
+ );
+ }
+ if (!repo || !repoId) return null;
+
+ return (
+
+
+ setBranch(e.target.value)} style={{ minWidth: 160 }}>
+ {branches.map((b) => (
+
+ {b}
+
+ ))}
+
+
+ }
+ />
+
+
+
+
+
+
+ Hash
+ Message
+ Author
+ Date
+ Files
+
+
+
+ {commits.map((c) => (
+ selectCommit(c.hash)}
+ >
+
+ {c.shortHash}
+
+ {c.message}
+ {c.author}
+ {new Date(c.date).toLocaleString()}
+ {c.changedFilesCount}
+
+ ))}
+
+
+
+
+
+ {selected ? (
+ <>
+
+ {selected.shortHash}
+
+ {selected.message}
+
+ Author
+ {selected.author}
+ Date
+ {new Date(selected.date).toLocaleString()}
+ Changed files
+ {selected.changedFilesCount}
+
+
+ Changed files
+
+ {selected.changedFiles.map((f: ChangedFile) => (
+
+ {f.status} {f.path}
+
+ ))}
+
+
+
+ {
+ if (selected.parents[0]) openDelta(selected.parents[0], selected.hash);
+ }}
+ >
+ Open in Delta View
+
+ openTrace(selected.hash)}>
+ Open in Trace View
+
+ openPanorama(selected.hash)}>
+ Open in Panorama
+
+
+ >
+ ) : (
+ Select a commit to view details and quick actions.
+ )}
+
+
+
+
+ Need another repository? Import again
+
+
+ );
+}
diff --git a/apps/web/src/pages/TraceViewPage.tsx b/apps/web/src/pages/TraceViewPage.tsx
new file mode 100644
index 000000000..ed1397e45
--- /dev/null
+++ b/apps/web/src/pages/TraceViewPage.tsx
@@ -0,0 +1,447 @@
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
+import { api, type TraceAnswer } from '../api/client';
+import {
+ Alert,
+ Badge,
+ Button,
+ Card,
+ CardHeader,
+ FormField,
+ Mono,
+ PageHeader,
+ Select,
+ TextArea,
+} from '../components/ui';
+import type { TraceEvidenceItem } from '../types';
+import { clearTraceSession, loadTraceSession, saveTraceSession } from '../lib/trace-cache';
+
+const EVIDENCE_KIND_LABEL: Record = {
+ 'commit-message': 'Commit message',
+ 'changed-file': 'Changed file',
+ 'changed-symbol': 'Changed symbol',
+ 'edge-change': 'Dependency edge',
+ 'risk-tag': 'Risk tag',
+ 'entry-point': 'Entry point',
+ 'code-diff': 'Code diff',
+ 'delta-summary': 'Delta summary',
+ 'delta-unavailable': 'Delta unavailable',
+};
+
+const EVOLUTION_LABEL: Record = {
+ before: 'Before',
+ candidate: 'Candidate',
+ after: 'After',
+ current: 'Current',
+};
+
+function confidenceHint(level: string): string {
+ switch (level) {
+ case 'high':
+ return 'Strong match to your question; verify in Delta View first.';
+ case 'medium':
+ return 'Moderate signals; cross-check candidates and diffs.';
+ default:
+ return 'Weak or short history; treat as directional only.';
+ }
+}
+
+function formatProviderNote(result: TraceAnswer): string | null {
+ const p = result.provider;
+ if (!p?.used) return null;
+ if (p.nonAuthoritativeText) {
+ return 'Model output failed validation; showing deterministic analysis only.';
+ }
+ return `Refined with ${p.type}${p.model ? ` (${p.model})` : ''}; evidence and Delta verification remain authoritative.`;
+}
+
+function groupEvidenceByCommit(evidence: TraceEvidenceItem[]): Map {
+ const map = new Map();
+ for (const ev of evidence) {
+ const list = map.get(ev.commitHash) ?? [];
+ list.push(ev);
+ map.set(ev.commitHash, list);
+ }
+ return map;
+}
+
+export default function TraceViewPage() {
+ const { repoId } = useParams<{ repoId: string }>();
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const candidate = searchParams.get('candidate') ?? '';
+
+ const [branches, setBranches] = useState([]);
+ const [question, setQuestion] = useState('');
+ const [branch, setBranch] = useState('');
+ const [commitLimit, setCommitLimit] = useState(50);
+ const [includeDiffEvidence, setIncludeDiffEvidence] = useState(true);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+ const [result, setResult] = useState(null);
+ const [restored, setRestored] = useState(false);
+
+ const persist = useCallback(
+ (next: TraceAnswer, q: string, b: string, limit: number, diffEv: boolean) => {
+ if (!repoId) return;
+ saveTraceSession(repoId, {
+ question: q,
+ branch: b,
+ commitLimit: limit,
+ includeDiffEvidence: diffEv,
+ result: next,
+ });
+ },
+ [repoId],
+ );
+
+ useEffect(() => {
+ if (!repoId) return;
+ const cached = loadTraceSession(repoId);
+ if (cached) {
+ setQuestion(cached.question);
+ setBranch(cached.branch);
+ setCommitLimit(cached.commitLimit);
+ setIncludeDiffEvidence(cached.includeDiffEvidence);
+ setResult(cached.result);
+ setRestored(true);
+ } else if (candidate) {
+ setQuestion(`Which commit likely introduced an issue related to ${candidate.slice(0, 7)}?`);
+ }
+ }, [repoId, candidate]);
+
+ useEffect(() => {
+ if (!repoId) return;
+ api
+ .listBranches(repoId)
+ .then((items) => {
+ setBranches(items);
+ setBranch((prev) => prev || items[0] || '');
+ })
+ .catch(() => setBranches([]));
+ }, [repoId]);
+
+ async function runTrace() {
+ if (!repoId || !question.trim()) return;
+ setLoading(true);
+ setError(null);
+ setRestored(false);
+ try {
+ const data = await api.runTrace(repoId, {
+ question: question.trim(),
+ branch: branch || undefined,
+ commitLimit,
+ includeDiffEvidence,
+ });
+ setResult(data);
+ persist(data, question.trim(), branch, commitLimit, includeDiffEvidence);
+ } catch (err) {
+ setResult(null);
+ if (repoId) clearTraceSession(repoId);
+ setError(err instanceof Error ? err.message : 'Trace failed');
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ function openDelta(baseHash: string, headHash: string) {
+ if (!repoId) return;
+ if (result) {
+ persist(result, question, branch, commitLimit, includeDiffEvidence);
+ }
+ navigate(`/repos/${repoId}/delta?base=${baseHash}&head=${headHash}&from=trace`);
+ }
+
+ function openPanorama(_baseHash: string, headHash: string) {
+ if (!repoId) return;
+ if (result) {
+ persist(result, question, branch, commitLimit, includeDiffEvidence);
+ }
+ const symbols = result?.impactRadius.symbols.slice(0, 12).join(',') ?? '';
+ const entryPoints = result?.impactRadius.entryPoints.slice(0, 8).join(',') ?? '';
+ const q = new URLSearchParams({
+ commit: headHash,
+ highlight: 'trace',
+ from: 'trace',
+ });
+ if (branch) q.set('branch', branch);
+ if (symbols) q.set('traceSymbols', symbols);
+ if (entryPoints) q.set('traceEntryPoints', entryPoints);
+ navigate(`/repos/${repoId}/panorama?${q.toString()}`);
+ }
+
+ const topCandidate = result?.candidates[0];
+ const providerNote = result ? formatProviderNote(result) : null;
+ const evidenceByCommit = useMemo(
+ () => (result ? groupEvidenceByCommit(result.evidence) : new Map()),
+ [result],
+ );
+
+ const userFacingUncertainty = useMemo(() => {
+ if (!result) return [];
+ return result.uncertainty.filter(
+ (u) => !u.startsWith('Provider failed') && !u.startsWith('Provider output rejected'),
+ );
+ }, [result]);
+
+ const providerWarnings = useMemo(() => {
+ if (!result) return [];
+ return result.uncertainty.filter(
+ (u) => u.startsWith('Provider failed') || u.startsWith('Provider output rejected'),
+ );
+ }, [result]);
+
+ return (
+
+
+
+ {restored && result && (
+
Restored your previous trace results (navigation-safe).
+ )}
+
+
+
+
+
+
+ setBranch(e.target.value)}>
+ Default branch
+ {branches.map((b) => (
+
+ {b}
+
+ ))}
+
+
+
+ setCommitLimit(Number(e.target.value))}>
+ 30
+ 50
+ 80
+ 120
+
+
+
+ setIncludeDiffEvidence(e.target.value === 'yes')}
+ >
+ Yes
+ No
+
+
+
+
+
+ {loading ? 'Tracing…' : 'Run trace'}
+
+ {result && (
+ {
+ setResult(null);
+ setRestored(false);
+ if (repoId) clearTraceSession(repoId);
+ }}
+ >
+ Clear results
+
+ )}
+
+
+
+ {error &&
{error} }
+
+ {result && (
+ <>
+
+
+
+
+
{result.directAnswer}
+
{confidenceHint(result.confidence)}
+ {result.mostLikelyCommit && (
+
+ {result.mostLikelyCommit.shortHash}
+ — {result.mostLikelyCommit.message}
+
+ )}
+ {providerNote &&
{providerNote}
}
+
+
+ Confidence: {result.confidence}
+ {topCandidate?.previousCommitHash && result.mostLikelyCommit && (
+ <>
+ openDelta(topCandidate.previousCommitHash!, result.mostLikelyCommit!.hash)}
+ >
+ Verify in Delta View
+
+
+ openPanorama(topCandidate.previousCommitHash!, result.mostLikelyCommit!.hash)
+ }
+ >
+ View in Panorama
+
+ >
+ )}
+
+
+ {providerWarnings.length > 0 && (
+
+
+ {providerWarnings.map((w, i) => (
+ {w}
+ ))}
+
+
+ Candidates and evidence below are still valid. Ensure codex login{' '}
+ works on this machine, then restart npm run dev:codedelta and retry.
+
+
+ )}
+
+
+
+
+
+ {result.candidates.map((c, idx) => (
+
+
+ #{idx + 1}
+
+ {c.commit.shortHash}
+
+ Score {c.relevanceScore}
+
+ {c.commit.message}
+ {c.reasons.join(' · ')}
+ {c.changedFiles.length > 0 && (
+
+ Files: {c.changedFiles
+ .slice(0, 5)
+ .map((f) => f.path)
+ .join(', ')}
+ {c.changedFiles.length > 5 ? ` (+${c.changedFiles.length - 5} more)` : ''}
+
+ )}
+ {c.previousCommitHash ? (
+ <>
+ openDelta(c.previousCommitHash!, c.commit.hash)}>
+ Compare parent → this commit in Delta
+
+ openPanorama(c.previousCommitHash!, c.commit.hash)}>
+ View in Panorama
+
+ >
+ ) : (
+ No parent commit for structural comparison.
+ )}
+
+ ))}
+
+
+
+
+ Change timeline
+
+
+ {result.evolution.map((s, i) => (
+
+ {EVOLUTION_LABEL[s.label] ?? s.label}
+ {s.commitHash ? ` (${s.commitHash.slice(0, 7)})` : ''} — {s.summary}
+
+ ))}
+
+
+
+
+
+
+ Impact overview ({result.impactRadius.files.length} files · {result.impactRadius.symbols.length}{' '}
+ symbols)
+
+
+
Risk tags: {result.impactRadius.riskTags.join(', ') || 'none'}
+
+ Entry points: {result.impactRadius.entryPoints.slice(0, 8).join(', ') || 'none detected'}
+
+
+
+
+ {(userFacingUncertainty.length > 0 || result.suggestedNextChecks.length > 0) && (
+
+ Uncertainty and next steps
+
+ {userFacingUncertainty.length > 0 && (
+
+ {userFacingUncertainty.map((u, i) => (
+ {u}
+ ))}
+
+ )}
+ {result.suggestedNextChecks.length > 0 && (
+ <>
+
Suggested checks
+
+ {result.suggestedNextChecks.map((s, i) => (
+ {s}
+ ))}
+
+ >
+ )}
+
+
+ )}
+
+
+ Evidence details ({result.evidence.length} items)
+
+
Each item maps to a commit. Open Delta for code-level verification.
+ {Array.from(evidenceByCommit.entries()).map(([hash, items]: [string, TraceEvidenceItem[]]) => (
+
+
+ {hash.slice(0, 7)}
+
+
+ {items.map((ev) => (
+
+ {EVIDENCE_KIND_LABEL[ev.kind] ?? ev.kind}
+ {' — '}
+ {ev.title}
+ {ev.file && ({ev.file}) }
+
+ ))}
+
+
+ ))}
+
+
+ >
+ )}
+
+ {!result && !loading && !error && (
+
+ Enter a specific question and run trace. You can also start from{' '}
+ Commit Timeline.
+
+ )}
+
+ );
+}
diff --git a/apps/web/src/pages/WikiPage.tsx b/apps/web/src/pages/WikiPage.tsx
new file mode 100644
index 000000000..c5a495155
--- /dev/null
+++ b/apps/web/src/pages/WikiPage.tsx
@@ -0,0 +1,396 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { Link, useParams, useSearchParams } from 'react-router-dom';
+import {
+ api,
+ type CommitInfo,
+ type WikiAskAnswer,
+ type WikiPageContent,
+ type WikiStatus,
+ type WikiToc,
+} from '../api/client';
+import WikiMarkdown from '../components/WikiMarkdown';
+import { Alert, Button, Card, FormField, PageHeader, Select } from '../components/ui';
+
+interface ChatMessage {
+ role: 'user' | 'assistant';
+ content: string;
+ citations?: WikiAskAnswer['citations'];
+ confidence?: WikiAskAnswer['confidence'];
+ providerUsed?: boolean;
+}
+
+function chatStorageKey(repoId: string, commit: string): string {
+ return `codedelta-wiki-chat-${repoId}-${commit}`;
+}
+
+function loadChat(repoId: string, commit: string): ChatMessage[] {
+ try {
+ const raw = sessionStorage.getItem(chatStorageKey(repoId, commit));
+ return raw ? (JSON.parse(raw) as ChatMessage[]) : [];
+ } catch {
+ return [];
+ }
+}
+
+function saveChat(repoId: string, commit: string, messages: ChatMessage[]): void {
+ try {
+ sessionStorage.setItem(chatStorageKey(repoId, commit), JSON.stringify(messages.slice(-30)));
+ } catch {
+ // sessionStorage full/unavailable — chat just won't persist.
+ }
+}
+
+export default function WikiPage() {
+ const { repoId } = useParams<{ repoId: string }>();
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const [commits, setCommits] = useState([]);
+ const [commit, setCommit] = useState(searchParams.get('commit') ?? '');
+ const [status, setStatus] = useState(null);
+ const [toc, setToc] = useState(null);
+ const [page, setPage] = useState(null);
+ const [pageLoading, setPageLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ const [question, setQuestion] = useState('');
+ const [asking, setAsking] = useState(false);
+ const [messages, setMessages] = useState([]);
+
+ const activeSection = searchParams.get('section') ?? 'overview';
+ const pollRef = useRef(null);
+
+ // Bootstrap: repo → default branch commits; pick commit from URL or head.
+ useEffect(() => {
+ if (!repoId) return;
+ let cancelled = false;
+ (async () => {
+ try {
+ const repo = await api.getRepo(repoId);
+ const list = await api.listCommits(repoId, repo.defaultBranch, 80);
+ if (cancelled) return;
+ setCommits(list);
+ setCommit((prev) => prev || (searchParams.get('commit') ?? list[0]?.hash ?? ''));
+ } catch (err) {
+ if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load repository');
+ }
+ })();
+ return () => {
+ cancelled = true;
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [repoId]);
+
+ // Keep URL in sync with the selected commit.
+ useEffect(() => {
+ if (!commit) return;
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ next.set('commit', commit);
+ return prev.toString() === next.toString() ? prev : next;
+ });
+ }, [commit, setSearchParams]);
+
+ const refreshStatus = useCallback(async () => {
+ if (!repoId || !commit) return;
+ try {
+ const s = await api.getWikiStatus(repoId, commit);
+ setStatus(s);
+ if (s.state === 'ready') {
+ const t = await api.getWikiToc(repoId, commit);
+ setToc(t);
+ } else {
+ setToc(null);
+ }
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to load wiki status');
+ }
+ }, [repoId, commit]);
+
+ useEffect(() => {
+ setStatus(null);
+ setToc(null);
+ setPage(null);
+ setError(null);
+ if (repoId && commit) {
+ setMessages(loadChat(repoId, commit));
+ refreshStatus();
+ }
+ }, [repoId, commit, refreshStatus]);
+
+ // Poll while generating.
+ useEffect(() => {
+ if (status?.state !== 'generating') {
+ if (pollRef.current !== null) {
+ window.clearInterval(pollRef.current);
+ pollRef.current = null;
+ }
+ return;
+ }
+ if (pollRef.current === null) {
+ pollRef.current = window.setInterval(refreshStatus, 2000);
+ }
+ return () => {
+ if (pollRef.current !== null) {
+ window.clearInterval(pollRef.current);
+ pollRef.current = null;
+ }
+ };
+ }, [status?.state, refreshStatus]);
+
+ // Load the active section page when toc is ready.
+ useEffect(() => {
+ if (!repoId || !commit || !toc) return;
+ const section = toc.sections.find((s) => s.id === activeSection) ?? toc.sections[0];
+ if (!section) return;
+ let cancelled = false;
+ setPageLoading(true);
+ api
+ .getWikiPage(repoId, commit, section.id)
+ .then((p) => {
+ if (!cancelled) setPage(p);
+ })
+ .catch((err) => {
+ if (!cancelled) setError(err instanceof Error ? err.message : 'Failed to load wiki page');
+ })
+ .finally(() => {
+ if (!cancelled) setPageLoading(false);
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [repoId, commit, toc, activeSection]);
+
+ const handleGenerate = useCallback(async () => {
+ if (!repoId || !commit) return;
+ setError(null);
+ try {
+ await api.generateWiki(repoId, commit);
+ await refreshStatus();
+ } catch (err) {
+ setError(err instanceof Error ? err.message : 'Failed to start wiki generation');
+ }
+ }, [repoId, commit, refreshStatus]);
+
+ const handleAsk = useCallback(async () => {
+ if (!repoId || !commit || !question.trim() || asking) return;
+ const q = question.trim();
+ setQuestion('');
+ setAsking(true);
+ const withUser: ChatMessage[] = [...messages, { role: 'user', content: q }];
+ setMessages(withUser);
+ try {
+ const history = withUser
+ .slice(-7, -1)
+ .map((m) => ({ role: m.role, content: m.content }));
+ const result = await api.askWiki(repoId, { commit, question: q, history });
+ const withAnswer: ChatMessage[] = [
+ ...withUser,
+ {
+ role: 'assistant',
+ content: result.answer,
+ citations: result.citations,
+ confidence: result.confidence,
+ providerUsed: result.provider.used,
+ },
+ ];
+ setMessages(withAnswer);
+ saveChat(repoId, commit, withAnswer);
+ } catch (err) {
+ const withError: ChatMessage[] = [
+ ...withUser,
+ { role: 'assistant', content: `Ask failed: ${err instanceof Error ? err.message : 'unknown error'}` },
+ ];
+ setMessages(withError);
+ saveChat(repoId, commit, withError);
+ } finally {
+ setAsking(false);
+ }
+ }, [repoId, commit, question, asking, messages]);
+
+ const selectSection = useCallback(
+ (sectionId: string) => {
+ setSearchParams((prev) => {
+ const next = new URLSearchParams(prev);
+ next.set('section', sectionId);
+ return next;
+ });
+ },
+ [setSearchParams],
+ );
+
+ const selectedCommit = useMemo(() => commits.find((c) => c.hash === commit), [commits, commit]);
+
+ const generating = status?.state === 'generating';
+ const ready = status?.state === 'ready';
+
+ return (
+
+
+
+
+
+
+ setCommit(e.target.value)}>
+ Select commit…
+ {commits.map((c) => (
+
+ {c.shortHash} — {c.message.slice(0, 60)}
+
+ ))}
+
+
+ {!ready && (
+
+ {generating ? 'Generating…' : 'Generate Wiki'}
+
+ )}
+ {ready && (
+
+ Generated {status?.generatedAt ? new Date(status.generatedAt).toLocaleString() : ''} ·{' '}
+ {status?.llmUsed ? 'with LLM narration' : 'structural only (no LLM configured)'}
+
+ )}
+
+ {generating && (
+
+ Building wiki: {status?.completedSections ?? 0}/{status?.totalSections ?? '…'} sections
+ {status?.currentSection ? ` — ${status.currentSection}` : ''}. First run also builds the
+ commit snapshot, which can take a while on large repositories.
+
+ )}
+ {selectedCommit && (
+
+ Snapshot {selectedCommit.shortHash} · {selectedCommit.message.slice(0, 80)}
+
+ )}
+
+
+ {error &&
{error} }
+
+ {!ready && !generating && !error && (
+
+ No wiki for this commit yet. Generate one to get an overview, architecture diagrams from the
+ structural graph, and per-module pages. Without a configured provider you still get the
+ structural wiki; configure one in Provider Settings for
+ narrated pages and Ask answers.
+
+ )}
+
+ {ready && toc && (
+
+
+
+ Contents
+
+ {toc.sections.map((s) => (
+
+ selectSection(s.id)}
+ >
+ {s.title}
+ {s.kind === 'module' && ({s.symbolCount}) }
+
+
+ ))}
+
+
+
+
+
+
+ {pageLoading && Loading page…
}
+ {!pageLoading && page && }
+ {!pageLoading && page && page.citations.length > 0 && (
+
+ Symbols referenced on this page ({page.citations.length})
+
+ {page.citations.map((c) => (
+
+ {c.symbol ? (
+
+ {c.symbol}
+
+ ) : (
+ {c.file}
+ )}{' '}
+
+ {c.file}
+ {c.startLine !== undefined ? ` L${c.startLine}–L${c.endLine}` : ''}
+
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ )}
+
+ );
+}
diff --git a/apps/web/src/routes.tsx b/apps/web/src/routes.tsx
new file mode 100644
index 000000000..a31531f63
--- /dev/null
+++ b/apps/web/src/routes.tsx
@@ -0,0 +1,75 @@
+import { lazy, Suspense } from 'react';
+import { Navigate, Route, Routes } from 'react-router-dom';
+import AppShell from './components/AppShell';
+import ImportPage from './pages/ImportPage';
+
+// Route-level code splitting: heavy views (xyflow graph, markdown/mermaid) load on demand.
+const TimelinePage = lazy(() => import('./pages/TimelinePage'));
+const DeltaViewPage = lazy(() => import('./pages/DeltaViewPage'));
+const TraceViewPage = lazy(() => import('./pages/TraceViewPage'));
+const PanoramaPage = lazy(() => import('./pages/PanoramaPage'));
+const WikiPage = lazy(() => import('./pages/WikiPage'));
+const ProviderSettingsPage = lazy(() => import('./pages/ProviderSettingsPage'));
+
+function PageFallback() {
+ return Loading…
;
+}
+
+export function AppRoutes() {
+ return (
+
+ }>
+ } />
+ } />
+ }>
+
+
+ }
+ />
+ }>
+
+
+ }
+ />
+ }>
+
+
+ }
+ />
+ }>
+
+
+ }
+ />
+ }>
+
+
+ }
+ />
+ }>
+
+
+ }
+ />
+
+
+ );
+}
diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css
new file mode 100644
index 000000000..cba035d55
--- /dev/null
+++ b/apps/web/src/styles.css
@@ -0,0 +1,28 @@
+@import './styles/tokens.css';
+@import './styles/base.css';
+@import './styles/components.css';
+@import './styles/layout.css';
+@import './styles/boot.css';
+@import './styles/pages.css';
+
+/* Legacy aliases for gradual migration */
+.muted {
+ color: var(--text-muted);
+}
+
+.hint {
+ color: var(--text-muted);
+ font-size: 0.8125rem;
+ line-height: 1.45;
+}
+
+.lead {
+ color: var(--text-muted);
+ max-width: 65ch;
+ margin-bottom: var(--space-3);
+ font-size: 0.9375rem;
+}
+
+.mt {
+ margin-top: var(--space-4);
+}
diff --git a/apps/web/src/styles/base.css b/apps/web/src/styles/base.css
new file mode 100644
index 000000000..0e70970d7
--- /dev/null
+++ b/apps/web/src/styles/base.css
@@ -0,0 +1,71 @@
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html {
+ -webkit-font-smoothing: antialiased;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ font-family: var(--font-sans);
+ font-size: 14px;
+ line-height: 1.5;
+ color: var(--text);
+ background: var(--bg-app);
+}
+
+a {
+ color: var(--accent);
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+code,
+.mono {
+ font-family: var(--font-mono);
+ font-size: 0.9em;
+}
+
+h1,
+h2,
+h3 {
+ margin: 0 0 var(--space-2);
+ font-weight: 600;
+ letter-spacing: -0.02em;
+}
+
+h1 {
+ font-size: 1.5rem;
+}
+
+h2 {
+ font-size: 1.125rem;
+}
+
+h3 {
+ font-size: 0.95rem;
+}
+
+p {
+ margin: 0 0 var(--space-3);
+}
+
+ul {
+ margin: 0;
+}
+
+button:focus-visible,
+a:focus-visible,
+select:focus-visible,
+input:focus-visible,
+textarea:focus-visible {
+ outline: none;
+ box-shadow: var(--focus-ring);
+}
diff --git a/apps/web/src/styles/boot.css b/apps/web/src/styles/boot.css
new file mode 100644
index 000000000..0599aa96e
--- /dev/null
+++ b/apps/web/src/styles/boot.css
@@ -0,0 +1,60 @@
+.boot-screen {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--space-6);
+ background: var(--bg);
+}
+
+.boot-card {
+ width: min(420px, 100%);
+ padding: var(--space-8);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ background: var(--bg-surface);
+ box-shadow: var(--shadow-md);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-4);
+}
+
+.boot-title {
+ margin: 0;
+ font-size: 1.5rem;
+ font-weight: 700;
+ letter-spacing: -0.03em;
+}
+
+.boot-message {
+ margin: 0;
+ color: var(--text-muted);
+}
+
+.boot-hint {
+ margin: 0;
+ font-size: 0.8125rem;
+}
+
+.boot-git-banner {
+ padding: var(--space-2) var(--space-6);
+ background: #fff8e6;
+ border-bottom: 1px solid #e6d9a8;
+ color: #6b5700;
+ font-size: 0.875rem;
+}
+
+.recent-repos-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+}
+
+.recent-repos-list li button {
+ width: 100%;
+ text-align: left;
+ justify-content: flex-start;
+}
diff --git a/apps/web/src/styles/components.css b/apps/web/src/styles/components.css
new file mode 100644
index 000000000..415a45bdb
--- /dev/null
+++ b/apps/web/src/styles/components.css
@@ -0,0 +1,485 @@
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ border-radius: var(--radius-md);
+ font-family: inherit;
+ font-size: 0.875rem;
+ font-weight: 500;
+ line-height: 1.25;
+ cursor: pointer;
+ border: 1px solid transparent;
+ transition: background 0.12s, border-color 0.12s, color 0.12s;
+}
+
+.btn:disabled {
+ opacity: 0.55;
+ cursor: not-allowed;
+}
+
+.btn-md {
+ padding: 0.45rem 0.9rem;
+ min-height: 36px;
+}
+
+.btn-sm {
+ padding: 0.3rem 0.65rem;
+ min-height: 30px;
+ font-size: 0.8125rem;
+}
+
+.btn-primary {
+ background: var(--accent);
+ color: #fff;
+ border-color: rgba(31, 35, 40, 0.12);
+ box-shadow: var(--shadow-sm);
+}
+
+.btn-primary:hover:not(:disabled) {
+ background: var(--accent-hover);
+}
+
+.btn-secondary {
+ background: var(--bg-surface);
+ color: var(--text);
+ border-color: var(--border);
+ box-shadow: var(--shadow-sm);
+}
+
+.btn-secondary:hover:not(:disabled) {
+ background: var(--bg-muted);
+}
+
+.btn-ghost {
+ background: transparent;
+ color: var(--accent);
+ border-color: transparent;
+}
+
+.btn-ghost:hover:not(:disabled) {
+ background: var(--accent-subtle);
+}
+
+.btn-link {
+ background: none;
+ border: none;
+ color: var(--accent);
+ padding: 0;
+ min-height: auto;
+ font-weight: 500;
+ text-align: left;
+}
+
+.btn-link:hover:not(:disabled) {
+ text-decoration: underline;
+}
+
+.card {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ box-shadow: var(--shadow-sm);
+ padding: var(--space-5);
+ margin-bottom: var(--space-4);
+}
+
+.card-header {
+ margin-bottom: var(--space-4);
+}
+
+.card-header h2,
+.card-header h3 {
+ margin-bottom: var(--space-1);
+}
+
+.card-muted {
+ background: var(--bg-muted);
+ border-style: dashed;
+}
+
+.badge {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.12rem 0.5rem;
+ border-radius: 999px;
+ font-size: 0.75rem;
+ font-weight: 500;
+ border: 1px solid var(--border);
+ background: var(--bg-muted);
+ color: var(--text);
+}
+
+.badge-accent {
+ background: var(--accent-subtle);
+ border-color: var(--accent-border);
+ color: var(--accent);
+}
+
+.badge-success {
+ background: var(--success-bg);
+ border-color: var(--success-border);
+ color: var(--success);
+}
+
+.badge-warning {
+ background: var(--warning-bg);
+ border-color: var(--warning-border);
+ color: var(--warning);
+}
+
+.badge-danger {
+ background: var(--danger-bg);
+ border-color: var(--danger-border);
+ color: var(--danger);
+}
+
+.badge-impact-low {
+ color: #1a7f37;
+ background: #dafbe1;
+ border-color: #4ac26b66;
+}
+
+.badge-impact-medium {
+ color: #9a6700;
+ background: #fff8c5;
+ border-color: #d4a72c66;
+}
+
+.badge-impact-high {
+ color: #bc4c00;
+ background: #fff1e5;
+ border-color: #fd8c7366;
+}
+
+.badge-impact-critical {
+ color: #a40e26;
+ background: #ffebe9;
+ border-color: #ff818266;
+}
+
+.alert {
+ padding: var(--space-3) var(--space-4);
+ border-radius: var(--radius-md);
+ border: 1px solid;
+ margin-bottom: var(--space-4);
+ font-size: 0.875rem;
+}
+
+.alert-error {
+ background: var(--danger-bg);
+ border-color: var(--danger-border);
+ color: var(--danger);
+}
+
+.alert-success {
+ background: var(--success-bg);
+ border-color: var(--success-border);
+ color: var(--success);
+}
+
+.alert-warning {
+ background: var(--warning-bg);
+ border-color: var(--warning-border);
+ color: var(--warning);
+}
+
+.form-field {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-1);
+ margin-bottom: var(--space-3);
+}
+
+.form-field label {
+ font-size: 0.8125rem;
+ font-weight: 500;
+ color: var(--text);
+}
+
+.form-hint {
+ font-size: 0.8125rem;
+ color: var(--text-muted);
+ margin: 0;
+}
+
+.input,
+.select,
+.textarea {
+ width: 100%;
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ color: var(--text);
+ padding: 0.45rem 0.65rem;
+ font: inherit;
+ font-size: 0.875rem;
+}
+
+.input:focus,
+.select:focus,
+.textarea:focus {
+ border-color: var(--accent);
+}
+
+.textarea {
+ resize: vertical;
+ min-height: 88px;
+}
+
+.form-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-3);
+ align-items: flex-end;
+}
+
+.form-row .form-field {
+ flex: 1;
+ min-width: 140px;
+ margin-bottom: 0;
+}
+
+.btn-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ margin-top: var(--space-3);
+}
+
+.data-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.8125rem;
+}
+
+.data-table th,
+.data-table td {
+ text-align: left;
+ padding: var(--space-2) var(--space-3);
+ border-bottom: 1px solid var(--border);
+}
+
+.data-table th {
+ font-weight: 600;
+ color: var(--text-muted);
+ background: var(--bg-muted);
+}
+
+.data-table tbody tr {
+ cursor: pointer;
+}
+
+.data-table tbody tr:hover {
+ background: var(--accent-subtle);
+}
+
+.data-table tbody tr.selected {
+ background: var(--accent-subtle);
+ box-shadow: inset 3px 0 0 var(--accent);
+}
+
+.segmented {
+ display: inline-flex;
+ padding: 3px;
+ background: var(--bg-muted);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ gap: 2px;
+ margin-bottom: var(--space-4);
+}
+
+.segmented button {
+ background: transparent;
+ border: none;
+ color: var(--text-muted);
+ padding: 0.35rem 0.75rem;
+ border-radius: var(--radius-sm);
+ font-size: 0.8125rem;
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.segmented button.active {
+ background: var(--bg-surface);
+ color: var(--text);
+ box-shadow: var(--shadow-sm);
+}
+
+.selectable-card {
+ display: flex;
+ gap: var(--space-3);
+ padding: var(--space-4);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ background: var(--bg-surface);
+ margin-bottom: var(--space-2);
+ transition: border-color 0.12s, box-shadow 0.12s;
+}
+
+.selectable-card:hover {
+ border-color: var(--border-strong);
+}
+
+.selectable-card.selected {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 1px var(--accent);
+ background: var(--accent-subtle);
+}
+
+.selectable-card input {
+ margin-top: 0.2rem;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ display: inline-block;
+ margin-right: var(--space-2);
+}
+
+.status-dot-ok {
+ background: var(--success);
+}
+
+.status-dot-off {
+ background: var(--text-subtle);
+}
+
+.empty-state {
+ text-align: center;
+ padding: var(--space-8) var(--space-4);
+ color: var(--text-muted);
+}
+
+.modal-backdrop {
+ position: fixed;
+ inset: 0;
+ background: rgba(31, 35, 40, 0.4);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 2000;
+ padding: var(--space-4);
+}
+
+.modal {
+ width: min(1100px, 100%);
+ max-height: 88vh;
+ overflow: auto;
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ box-shadow: var(--shadow-md);
+ padding: var(--space-5);
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: var(--space-4);
+ padding-bottom: var(--space-3);
+ border-bottom: 1px solid var(--border);
+}
+
+.diff-pre {
+ font-family: var(--font-mono);
+ font-size: 12px;
+ background: var(--bg-muted);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ padding: var(--space-3);
+ overflow: auto;
+}
+
+.diff-line {
+ white-space: pre;
+}
+
+.diff-line.added {
+ background: rgba(26, 127, 55, 0.12);
+}
+
+.diff-line.removed {
+ background: rgba(207, 34, 46, 0.1);
+}
+
+.diff-line.hunk {
+ background: var(--accent-subtle);
+ color: var(--accent);
+}
+
+.meta-grid {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: var(--space-1) var(--space-4);
+ font-size: 0.875rem;
+}
+
+.meta-grid dt {
+ color: var(--text-muted);
+ font-weight: 500;
+}
+
+.file-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ font-size: 0.875rem;
+}
+
+.file-list li {
+ padding: var(--space-1) 0;
+ border-bottom: 1px solid var(--bg-inset);
+}
+
+.file-list li:last-child {
+ border-bottom: none;
+}
+
+.chip-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+}
+
+.chip {
+ display: inline-block;
+ border: 1px solid var(--border);
+ border-radius: 999px;
+ padding: 0.15rem 0.55rem;
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ background: var(--bg-muted);
+}
+
+.risk-tag {
+ background: var(--danger-bg);
+ border: 1px solid var(--danger-border);
+ color: var(--danger);
+ border-radius: var(--radius-sm);
+ padding: 0.12rem 0.45rem;
+ font-size: 0.75rem;
+}
+
+.priority-high {
+ color: var(--danger);
+}
+
+.priority-medium {
+ color: var(--warning);
+}
+
+.priority-low {
+ color: var(--success);
+}
+
+.status-badge {
+ font-size: 0.75rem;
+ font-weight: 500;
+ text-transform: capitalize;
+ color: var(--text-muted);
+}
diff --git a/apps/web/src/styles/layout.css b/apps/web/src/styles/layout.css
new file mode 100644
index 000000000..ad2df8231
--- /dev/null
+++ b/apps/web/src/styles/layout.css
@@ -0,0 +1,211 @@
+.app {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.app-header {
+ height: var(--header-height);
+ border-bottom: 1px solid var(--border);
+ background: var(--bg-surface);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 var(--space-6);
+ box-shadow: var(--shadow-sm);
+}
+
+.app-header-brand {
+ display: flex;
+ align-items: baseline;
+ gap: var(--space-3);
+}
+
+.brand-link {
+ font-size: 1.125rem;
+ font-weight: 700;
+ color: var(--text);
+ text-decoration: none;
+ letter-spacing: -0.03em;
+}
+
+.brand-link:hover {
+ text-decoration: none;
+ color: var(--accent);
+}
+
+.brand-tagline {
+ font-size: 0.8125rem;
+ color: var(--text-muted);
+}
+
+.app-header-repo {
+ font-size: 0.8125rem;
+ color: var(--text-muted);
+ max-width: 360px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.app-header-repo strong {
+ color: var(--text);
+ font-weight: 600;
+}
+
+.app-body {
+ display: flex;
+ flex: 1;
+ min-height: 0;
+}
+
+.app-sidebar {
+ width: var(--sidebar-width);
+ flex-shrink: 0;
+ border-right: 1px solid var(--border);
+ background: var(--bg-surface);
+ padding: var(--space-4);
+ overflow-y: auto;
+}
+
+.nav-section {
+ margin-bottom: var(--space-6);
+}
+
+.nav-section-title {
+ font-size: 0.6875rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--text-subtle);
+ margin: 0 0 var(--space-2);
+}
+
+.nav-link {
+ display: block;
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-md);
+ color: var(--text);
+ text-decoration: none;
+ font-size: 0.875rem;
+ font-weight: 500;
+ margin-bottom: 2px;
+ border-left: 3px solid transparent;
+}
+
+.nav-link:hover {
+ background: var(--bg-muted);
+ text-decoration: none;
+}
+
+.nav-link.active {
+ background: var(--accent-subtle);
+ color: var(--accent);
+ border-left-color: var(--accent);
+}
+
+.nav-link.disabled {
+ color: var(--text-subtle);
+ cursor: not-allowed;
+ pointer-events: none;
+ opacity: 0.65;
+}
+
+.app-main {
+ flex: 1;
+ overflow: auto;
+ padding: var(--space-6);
+}
+
+.page {
+ max-width: var(--content-max);
+ margin: 0 auto;
+}
+
+.page-header {
+ margin-bottom: var(--space-5);
+}
+
+.page-lead {
+ color: var(--text-muted);
+ max-width: 65ch;
+ margin: 0;
+ font-size: 0.9375rem;
+}
+
+.page-grid-2 {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--space-4);
+}
+
+.split-layout {
+ display: grid;
+ grid-template-columns: 1fr 320px;
+ gap: var(--space-4);
+ align-items: start;
+}
+
+.sticky-panel {
+ position: sticky;
+ top: var(--space-4);
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-4);
+ box-shadow: var(--shadow-sm);
+}
+
+.back-link {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-1);
+ font-size: 0.875rem;
+ font-weight: 500;
+ margin-bottom: var(--space-4);
+}
+
+.footer-note {
+ margin-top: var(--space-8);
+ font-size: 0.8125rem;
+ color: var(--text-muted);
+}
+
+@media (max-width: 960px) {
+ .app-body {
+ flex-direction: column;
+ }
+
+ .app-sidebar {
+ width: 100%;
+ border-right: none;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ padding: var(--space-3);
+ }
+
+ .nav-section {
+ margin-bottom: 0;
+ margin-right: var(--space-4);
+ }
+
+ .nav-section-title {
+ display: none;
+ }
+
+ .nav-link {
+ display: inline-block;
+ border-left: none;
+ }
+
+ .page-grid-2,
+ .split-layout {
+ grid-template-columns: 1fr;
+ }
+
+ .brand-tagline {
+ display: none;
+ }
+}
diff --git a/apps/web/src/styles/pages.css b/apps/web/src/styles/pages.css
new file mode 100644
index 000000000..f298a4c94
--- /dev/null
+++ b/apps/web/src/styles/pages.css
@@ -0,0 +1,856 @@
+.delta-toolbar {
+ display: grid;
+ grid-template-columns: 1fr 1fr auto;
+ gap: var(--space-3);
+ align-items: end;
+ margin-bottom: var(--space-4);
+ padding: var(--space-4);
+ background: var(--bg-muted);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+}
+
+.impact-hero {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ margin-bottom: var(--space-2);
+}
+
+.impact-score {
+ font-size: 2.25rem;
+ font-weight: 700;
+ margin: 0;
+ color: var(--accent);
+ line-height: 1;
+}
+
+.summary-grid {
+ display: grid;
+ gap: var(--space-4);
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+}
+
+.overview-list {
+ margin: 0 0 var(--space-4);
+ padding-left: 1.25rem;
+ color: var(--text);
+}
+
+.panel-highlight {
+ border-color: var(--accent-border);
+ box-shadow: 0 0 0 1px var(--accent-subtle);
+}
+
+.trace-summary-layout {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: var(--space-5);
+ align-items: start;
+}
+
+@media (max-width: 768px) {
+ .trace-summary-layout {
+ grid-template-columns: 1fr;
+ }
+}
+
+.trace-direct-answer {
+ font-size: 1rem;
+ line-height: 1.6;
+ color: var(--text);
+}
+
+.trace-summary-aside {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: var(--space-3);
+ min-width: 200px;
+}
+
+.candidate-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.candidate-item {
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ padding: var(--space-4);
+ margin-bottom: var(--space-3);
+ background: var(--bg-surface);
+}
+
+.candidate-item-top {
+ border-color: var(--accent-border);
+ background: var(--accent-subtle);
+}
+
+.candidate-head {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ margin-bottom: var(--space-2);
+}
+
+.candidate-rank {
+ font-size: 0.75rem;
+ font-weight: 600;
+ color: var(--text-muted);
+ background: var(--bg-muted);
+ padding: 0.1rem 0.4rem;
+ border-radius: var(--radius-sm);
+}
+
+.candidate-score {
+ margin-left: auto;
+ font-size: 0.8125rem;
+ color: var(--text-muted);
+}
+
+.details-card {
+ padding: 0;
+ overflow: hidden;
+}
+
+.details-card summary {
+ cursor: pointer;
+ font-weight: 600;
+ padding: var(--space-4) var(--space-5);
+ list-style: none;
+ border-bottom: 1px solid transparent;
+}
+
+.details-card[open] summary {
+ border-bottom-color: var(--border);
+}
+
+.details-card summary::-webkit-details-marker {
+ display: none;
+}
+
+.details-card .details-body {
+ padding: var(--space-4) var(--space-5);
+}
+
+.evidence-group {
+ margin-top: var(--space-4);
+}
+
+.evidence-group h3 {
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+}
+
+.evidence-kind {
+ color: var(--accent);
+ font-size: 0.8125rem;
+ font-weight: 500;
+}
+
+.page--panorama {
+ max-width: none;
+ width: 100%;
+}
+
+.panorama-controls-card {
+ margin-bottom: var(--space-4);
+}
+
+.panorama-commit-hint {
+ margin: var(--space-3) 0 0;
+ padding-top: var(--space-3);
+ border-top: 1px solid var(--border);
+}
+
+.panorama-controls {
+ display: grid;
+ grid-template-columns: 1fr 2fr 1fr 2fr auto;
+ gap: var(--space-3);
+ align-items: end;
+}
+
+.panorama-intro {
+ margin: 0 0 var(--space-4);
+ max-width: 52rem;
+ line-height: 1.55;
+}
+
+.panorama-export-error {
+ margin: 0 0 var(--space-2);
+ color: var(--danger);
+}
+
+.panorama-overview-hint {
+ display: block;
+ margin-top: 0.25rem;
+ color: var(--text-muted);
+}
+
+@media (max-width: 960px) {
+ .panorama-controls {
+ grid-template-columns: 1fr;
+ }
+
+ .panorama-workspace {
+ margin-left: 0;
+ margin-right: 0;
+ width: 100%;
+ }
+}
+
+.panorama-workspace {
+ margin-left: calc(-1 * var(--space-6));
+ margin-right: calc(-1 * var(--space-6));
+ width: calc(100% + 2 * var(--space-6));
+}
+
+.panorama-panel {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-4);
+ box-shadow: var(--shadow-sm);
+ min-height: calc(100vh - 280px);
+}
+
+.panorama-status {
+ color: var(--text-muted);
+ padding: var(--space-6);
+ text-align: center;
+}
+
+.panorama-toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: var(--space-3);
+ padding-bottom: var(--space-3);
+ border-bottom: 1px solid var(--border);
+}
+
+.panorama-toolbar-info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.panorama-toolbar-title {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--text);
+}
+
+.panorama-breadcrumb {
+ margin-top: 0.125rem;
+}
+
+.panorama-breadcrumb-list {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.125rem 0;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ font-size: 0.8125rem;
+ line-height: 1.4;
+}
+
+.panorama-breadcrumb-item {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ max-width: 100%;
+}
+
+.panorama-breadcrumb-sep {
+ color: var(--text-muted);
+ user-select: none;
+ padding: 0 0.125rem;
+}
+
+.panorama-breadcrumb-link {
+ appearance: none;
+ border: none;
+ background: none;
+ padding: 0.125rem 0.25rem;
+ margin: 0;
+ border-radius: var(--radius-sm);
+ color: var(--accent);
+ font: inherit;
+ cursor: pointer;
+ text-align: left;
+ max-width: 16rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.panorama-breadcrumb-link:hover {
+ background: var(--bg-muted);
+ text-decoration: underline;
+}
+
+.panorama-breadcrumb-current {
+ color: var(--text);
+ font-weight: 500;
+ padding: 0.125rem 0.25rem;
+ max-width: 20rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.panorama-toolbar-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-2);
+ flex-shrink: 0;
+ align-items: center;
+}
+
+.panorama-flow {
+ width: 100%;
+ height: calc(100vh - 320px);
+ min-height: 640px;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ background: var(--bg-muted);
+ overflow: hidden;
+}
+
+.panorama-flow .react-flow svg {
+ overflow: visible;
+}
+
+.panorama-flow .react-flow__edge-path {
+ stroke: var(--accent);
+}
+
+.panorama-flow .react-flow__edge.panorama-edge--heuristic .react-flow__edge-path {
+ stroke: var(--text-subtle);
+ stroke-dasharray: 6 4;
+}
+
+.panorama-flow .react-flow__edge.panorama-edge--path .react-flow__edge-path {
+ stroke: #0550ae;
+}
+
+.panorama-controls-widget button {
+ background: var(--bg-surface);
+ border-color: var(--border);
+ color: var(--text);
+}
+
+.panorama-controls-widget button:hover {
+ background: var(--bg-inset);
+}
+
+.panorama-minimap {
+ background: var(--bg-surface) !important;
+ border: 1px solid var(--border);
+}
+
+.panorama-node {
+ position: relative;
+ width: 360px;
+ min-height: 168px;
+ padding: 0;
+ border-radius: 10px;
+ border: 1px solid var(--border);
+ background: var(--bg-surface);
+ box-shadow: var(--shadow-md);
+ font-size: 0.875rem;
+ overflow: hidden;
+}
+
+.panorama-node-accent {
+ height: 4px;
+ width: 100%;
+ background: var(--border-strong);
+}
+
+.panorama-node--kind-route .panorama-node-accent {
+ background: linear-gradient(90deg, #fb8500, #ffb703);
+}
+
+.panorama-node--kind-component .panorama-node-accent {
+ background: linear-gradient(90deg, #0969da, #54aeff);
+}
+
+.panorama-node--kind-callable .panorama-node-accent {
+ background: linear-gradient(90deg, #1a7f37, #3fb950);
+}
+
+.panorama-node--kind-class .panorama-node-accent {
+ background: linear-gradient(90deg, #8250df, #a371f7);
+}
+
+.panorama-node-top {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.35rem;
+ padding: 0.65rem 0.85rem 0.35rem;
+}
+
+.panorama-node-kind {
+ font-size: 0.6875rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: var(--text-muted);
+}
+
+.panorama-node-role {
+ font-size: 0.6875rem;
+ padding: 0.1rem 0.45rem;
+ border-radius: 999px;
+ background: var(--bg-muted);
+ color: var(--text);
+ border: 1px solid var(--border);
+}
+
+.panorama-node-title {
+ font-weight: 700;
+ font-size: 1.05rem;
+ color: var(--text);
+ padding: 0 0.85rem 0.5rem;
+ line-height: 1.25;
+ word-break: break-word;
+}
+
+.panorama-node-meta {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 0.35rem;
+ padding: 0 0.85rem 0.65rem;
+}
+
+.panorama-node-meta-item {
+ display: flex;
+ flex-direction: column;
+ gap: 0.1rem;
+ min-width: 0;
+}
+
+.panorama-meta-label {
+ font-size: 0.625rem;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--text-subtle);
+}
+
+.panorama-meta-value {
+ font-size: 0.75rem;
+ color: var(--text);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.panorama-meta-mono {
+ font-family: var(--font-mono);
+}
+
+.panorama-node-path {
+ margin: 0 0.85rem 0.65rem;
+ padding: 0.35rem 0.5rem;
+ border-radius: 6px;
+ background: var(--bg-muted);
+ border: 1px solid var(--border);
+ font-family: var(--font-mono);
+ font-size: 0.6875rem;
+ color: var(--text-muted);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.panorama-node-signature {
+ margin: 0 0.85rem 0.65rem;
+ padding: 0.4rem 0.5rem;
+ border-radius: 6px;
+ background: var(--accent-subtle);
+ border: 1px solid var(--accent-border);
+ font-family: var(--font-mono);
+ font-size: 0.6875rem;
+ color: #0550ae;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.panorama-node-label {
+ margin: 0 0.85rem 0.65rem;
+ padding: 0.45rem 0.55rem;
+ border-radius: 6px;
+ background: var(--accent-subtle);
+ border: 1px solid var(--accent-border);
+ color: var(--text);
+ font-size: 0.75rem;
+ line-height: 1.4;
+}
+
+.panorama-continue-btn {
+ display: block;
+ width: calc(100% - 1.7rem);
+ margin: 0 0.85rem 0.85rem;
+ padding: 0.45rem 0.65rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ border: 1px solid var(--accent);
+ border-radius: 6px;
+ background: var(--accent-subtle);
+ color: var(--accent);
+ cursor: pointer;
+}
+
+.panorama-continue-btn:hover {
+ background: #cce5ff;
+}
+
+.panorama-node--role-entry {
+ border-color: var(--accent-border);
+}
+
+.panorama-node--delta-added {
+ border-color: var(--success);
+}
+
+.panorama-node--delta-modified {
+ border-color: var(--warning);
+}
+
+.panorama-node--delta-removed {
+ border-color: var(--danger);
+ opacity: 0.92;
+}
+
+.panorama-node--trace {
+ box-shadow: 0 0 0 2px var(--accent-border);
+}
+
+.panorama-node--path {
+ box-shadow: 0 0 0 2px rgba(9, 105, 218, 0.35);
+}
+
+.panorama-delta-badge {
+ font-size: 0.625rem;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ padding: 0.1rem 0.4rem;
+ border-radius: 4px;
+ margin-left: auto;
+}
+
+.panorama-delta-badge--added {
+ background: var(--success-bg);
+ color: var(--success);
+}
+
+.panorama-delta-badge--modified {
+ background: var(--warning-bg);
+ color: var(--warning);
+}
+
+.panorama-delta-badge--removed {
+ background: var(--danger-bg);
+ color: var(--danger);
+}
+
+.panorama-warn {
+ color: var(--warning);
+ font-size: 0.8125rem;
+}
+
+.panorama-entry-sidebar {
+ padding: var(--space-3);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ background: var(--bg-muted);
+ color: var(--text);
+}
+
+.panorama-entry-sidebar h4 {
+ color: var(--text);
+ margin: 0 0 var(--space-2);
+}
+
+.panorama-entry-sidebar-hint {
+ margin: 0 0 var(--space-2);
+ font-size: 0.8125rem;
+}
+
+.panorama-entry-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ max-height: 220px;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.panorama-entry-list .btn-link {
+ text-align: left;
+ width: 100%;
+ font-size: 0.8125rem;
+ line-height: 1.35;
+ padding: 0.35rem 0.5rem;
+ border-radius: var(--radius-sm);
+}
+
+.panorama-entry-list .btn-link:hover {
+ background: var(--bg);
+}
+
+.panorama-entry-kind {
+ display: inline-block;
+ min-width: 4.5rem;
+ margin-right: 0.5rem;
+ font-size: 0.6875rem;
+ text-transform: uppercase;
+ letter-spacing: 0.03em;
+ color: var(--text-muted);
+}
+
+.panorama-entry-list-item--in-graph .panorama-entry-kind {
+ color: var(--accent);
+}
+
+.panorama-legend {
+ display: flex;
+ flex-wrap: wrap;
+ gap: var(--space-3);
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ padding-top: var(--space-2);
+}
+
+.panorama-legend-added::before,
+.panorama-legend-modified::before,
+.panorama-legend-removed::before,
+.panorama-legend-heuristic::before {
+ content: '';
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin-right: 0.35rem;
+ border-radius: 2px;
+ vertical-align: middle;
+}
+
+.panorama-legend-added::before {
+ background: var(--success);
+}
+
+.panorama-legend-modified::before {
+ background: var(--warning);
+}
+
+.panorama-legend-removed::before {
+ background: var(--danger);
+}
+
+.panorama-legend-heuristic::before {
+ background: transparent;
+ border: 1px dashed var(--text-subtle);
+}
+
+.panorama-handle {
+ opacity: 0;
+ width: 8px;
+ height: 8px;
+}
+
+.panorama-error {
+ color: var(--danger);
+}
+
+/* --- Wiki --- */
+
+.wiki-toolbar {
+ display: flex;
+ align-items: flex-end;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+
+.wiki-meta-hint {
+ margin-bottom: 0.4rem;
+}
+
+.wiki-layout {
+ display: grid;
+ grid-template-columns: 220px minmax(0, 1fr) 320px;
+ gap: 1rem;
+ align-items: start;
+ margin-top: 1rem;
+}
+
+@media (max-width: 1100px) {
+ .wiki-layout {
+ grid-template-columns: 1fr;
+ }
+}
+
+.wiki-toc-list {
+ list-style: none;
+ margin: 0.5rem 0 0;
+ padding: 0;
+}
+
+.wiki-toc-link {
+ display: block;
+ width: 100%;
+ text-align: left;
+ background: none;
+ border: none;
+ padding: 0.35rem 0.5rem;
+ border-radius: 6px;
+ cursor: pointer;
+ color: var(--text);
+ font-size: 0.9rem;
+}
+
+.wiki-toc-link:hover {
+ background: var(--surface-hover, rgba(127, 127, 127, 0.12));
+}
+
+.wiki-toc-link.active {
+ background: var(--surface-hover, rgba(127, 127, 127, 0.16));
+ font-weight: 600;
+}
+
+.wiki-markdown {
+ line-height: 1.6;
+ overflow-wrap: anywhere;
+}
+
+.wiki-markdown h1 {
+ font-size: 1.4rem;
+}
+
+.wiki-markdown h2 {
+ font-size: 1.15rem;
+ margin-top: 1.4rem;
+}
+
+.wiki-markdown table {
+ border-collapse: collapse;
+ width: 100%;
+ font-size: 0.85rem;
+}
+
+.wiki-markdown th,
+.wiki-markdown td {
+ border: 1px solid var(--border);
+ padding: 0.3rem 0.5rem;
+ text-align: left;
+}
+
+.wiki-markdown pre {
+ overflow-x: auto;
+ padding: 0.6rem;
+ border-radius: 8px;
+ background: var(--surface-subtle, rgba(127, 127, 127, 0.08));
+}
+
+.wiki-markdown blockquote {
+ margin: 0.5rem 0;
+ padding: 0.25rem 0.75rem;
+ border-left: 3px solid var(--border);
+ color: var(--text-subtle);
+}
+
+.wiki-markdown img {
+ max-width: 100%;
+ height: auto;
+ border-radius: 6px;
+}
+
+.wiki-markdown a img {
+ border: none;
+}
+
+.wiki-mermaid {
+ overflow-x: auto;
+ margin: 0.75rem 0;
+}
+
+.wiki-mermaid svg {
+ max-width: 100%;
+ height: auto;
+}
+
+.wiki-citations {
+ margin-top: 1rem;
+ font-size: 0.85rem;
+}
+
+.wiki-citations ul {
+ margin: 0.5rem 0 0;
+ padding-left: 1.1rem;
+}
+
+.wiki-chat {
+ display: flex;
+ flex-direction: column;
+ gap: 0.6rem;
+ max-height: 50vh;
+ overflow-y: auto;
+ margin: 0.5rem 0;
+}
+
+.wiki-chat-msg {
+ border-radius: 10px;
+ padding: 0.5rem 0.7rem;
+ font-size: 0.88rem;
+}
+
+.wiki-chat-msg--user {
+ background: var(--surface-hover, rgba(127, 127, 127, 0.14));
+ align-self: flex-end;
+ max-width: 90%;
+}
+
+.wiki-chat-msg--user p {
+ margin: 0;
+}
+
+.wiki-chat-msg--assistant {
+ background: var(--surface-subtle, rgba(127, 127, 127, 0.07));
+}
+
+.wiki-chat-citations {
+ margin: 0.4rem 0 0;
+ padding-left: 1.1rem;
+ font-size: 0.8rem;
+}
+
+.wiki-ask-input {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.wiki-ask-input textarea {
+ width: 100%;
+ resize: vertical;
+ border-radius: 8px;
+ border: 1px solid var(--border);
+ background: var(--surface, transparent);
+ color: var(--text);
+ padding: 0.5rem;
+ font: inherit;
+ font-size: 0.88rem;
+}
diff --git a/apps/web/src/styles/tokens.css b/apps/web/src/styles/tokens.css
new file mode 100644
index 000000000..3dd495f53
--- /dev/null
+++ b/apps/web/src/styles/tokens.css
@@ -0,0 +1,42 @@
+:root {
+ --bg-app: #f6f8fa;
+ --bg-surface: #ffffff;
+ --bg-muted: #f0f3f6;
+ --bg-inset: #eaeef2;
+ --border: #d0d7de;
+ --border-strong: #afb8c1;
+ --text: #1f2328;
+ --text-muted: #656d76;
+ --text-subtle: #8c959f;
+ --accent: #0969da;
+ --accent-hover: #0860ca;
+ --accent-subtle: #ddf4ff;
+ --accent-border: #54aeff66;
+ --success: #1a7f37;
+ --success-bg: #dafbe1;
+ --success-border: #4ac26b66;
+ --warning: #9a6700;
+ --warning-bg: #fff8c5;
+ --warning-border: #d4a72c66;
+ --danger: #cf222e;
+ --danger-bg: #ffebe9;
+ --danger-border: #ff818266;
+ --radius-sm: 6px;
+ --radius-md: 8px;
+ --radius-lg: 12px;
+ --shadow-sm: 0 1px 0 rgba(31, 35, 40, 0.04);
+ --shadow-md: 0 3px 6px rgba(140, 149, 159, 0.15);
+ --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
+ --space-1: 0.25rem;
+ --space-2: 0.5rem;
+ --space-3: 0.75rem;
+ --space-4: 1rem;
+ --space-5: 1.25rem;
+ --space-6: 1.5rem;
+ --space-8: 2rem;
+ --sidebar-width: 240px;
+ --header-height: 56px;
+ --content-max: 1200px;
+ --focus-ring: 0 0 0 3px rgba(9, 105, 218, 0.35);
+}
diff --git a/apps/web/src/types.ts b/apps/web/src/types.ts
new file mode 100644
index 000000000..ff00a7d8d
--- /dev/null
+++ b/apps/web/src/types.ts
@@ -0,0 +1,399 @@
+/** Mirror of @codedelta/types for the web client. */
+
+export interface RepoRef {
+ id: string;
+ source: 'github' | 'local';
+ input: string;
+ clonePath: string;
+ defaultBranch: string;
+ remoteUrl?: string;
+ importedAt: string;
+}
+
+export interface CommitInfo {
+ hash: string;
+ shortHash: string;
+ message: string;
+ author: string;
+ authorEmail: string;
+ date: string;
+ parents: string[];
+ changedFilesCount: number;
+ impactScore?: number;
+}
+
+export interface ChangedFile {
+ path: string;
+ status: 'added' | 'modified' | 'deleted' | 'renamed' | 'copied';
+ oldPath?: string;
+}
+
+export interface CodeNode {
+ id: string;
+ kind: string;
+ name: string;
+ qualifiedName: string;
+ filePath: string;
+ language: string;
+ startLine: number;
+ endLine: number;
+ signature?: string;
+ isExported?: boolean;
+}
+
+export interface CodeEdge {
+ source: string;
+ target: string;
+ kind: string;
+ line?: number;
+}
+
+export interface GraphDiff {
+ baseCommit: string;
+ headCommit: string;
+ addedNodes: CodeNode[];
+ removedNodes: CodeNode[];
+ modifiedNodes: Array<{ before: CodeNode; after: CodeNode; changes: string[] }>;
+ addedEdges: CodeEdge[];
+ removedEdges: CodeEdge[];
+ affectedNodeIds: string[];
+ changedFiles: ChangedFile[];
+ summary: {
+ symbolsAdded: number;
+ symbolsRemoved: number;
+ symbolsModified: number;
+ edgesAdded: number;
+ edgesRemoved: number;
+ };
+}
+
+export type ImpactSeverity = 'low' | 'medium' | 'high' | 'critical';
+
+export interface ImpactSummary {
+ commitHash: string;
+ score: number;
+ changedSymbols: number;
+ changedEdges: number;
+ affectedModules: string[];
+ impactedEntryPoints: string[];
+ riskTags: string[];
+ explanation?: {
+ severity: ImpactSeverity;
+ summary: string;
+ reasons: string[];
+ topContributors: Array<{
+ factor: 'changedFiles' | 'changedSymbols' | 'changedEdges' | 'affectedNodes' | 'riskTags' | 'entryPoints';
+ value: number;
+ weight: number;
+ contribution: number;
+ }>;
+ };
+}
+
+export interface DeltaSummary {
+ title: string;
+ overview: string[];
+ mainAreas: Array<{
+ name: string;
+ files: string[];
+ changedSymbols: number;
+ riskTags: string[];
+ }>;
+ risks: Array<{
+ tag: string;
+ reason: string;
+ files: string[];
+ }>;
+ reviewOrder: Array<{
+ file: string;
+ reason: string;
+ priority: 'high' | 'medium' | 'low';
+ }>;
+ metrics: {
+ changedFiles: number;
+ changedSymbols: number;
+ edgeChanges: number;
+ affectedNodes: number;
+ };
+}
+
+export type DeltaSource = {
+ type: 'commit';
+ commitHash: string;
+ label?: string;
+};
+
+export type ExtractionMethod = 'codegraph' | 'fallback';
+
+export interface CompareResponse {
+ repoId: string;
+ base: DeltaSource;
+ head: DeltaSource;
+ graphDiff: GraphDiff;
+ impact: ImpactSummary;
+ deltaSummary?: DeltaSummary;
+ baseMeta: { nodeCount: number; edgeCount: number; extractionMethod: ExtractionMethod };
+ headMeta: { nodeCount: number; edgeCount: number; extractionMethod: ExtractionMethod };
+}
+
+export interface FileDiffHunk {
+ oldStart: number;
+ oldLines: number;
+ newStart: number;
+ newLines: number;
+ header: string;
+ lines: string[];
+}
+
+export interface FileDiffResponse {
+ repoId: string;
+ base: string;
+ head: string;
+ file: string;
+ status: ChangedFile['status'];
+ patch: string;
+ hunks: FileDiffHunk[];
+}
+
+export interface CommitDetail extends CommitInfo {
+ changedFiles: ChangedFile[];
+}
+
+export interface ImportRepoRequest {
+ source: 'github' | 'local';
+ input: string;
+}
+
+export type PanoramaDeltaStatus = 'added' | 'removed' | 'modified' | 'unchanged';
+
+export interface PanoramaNode {
+ id: string;
+ kind: string;
+ name: string;
+ qualifiedName: string;
+ filePath: string;
+ startLine: number;
+ endLine: number;
+ signature?: string;
+ commitHash?: string;
+ commitShortHash?: string;
+ role?: 'entry' | 'bridge' | 'leaf';
+ deltaStatus?: PanoramaDeltaStatus;
+ traceHighlight?: boolean;
+ pathHighlight?: boolean;
+ llmLabel?: string;
+ position?: { x: number; y: number };
+}
+
+export interface PanoramaEdge {
+ id: string;
+ source: string;
+ target: string;
+ kind: string;
+ line?: number;
+ provenance?: string;
+ synthesizedBy?: string;
+ deltaStatus?: 'added' | 'removed' | 'unchanged';
+ pathHighlight?: boolean;
+}
+
+export interface PanoramaEntryCatalogItem {
+ id: string;
+ qualifiedName: string;
+ kind: string;
+ inGraph: boolean;
+}
+
+export interface PanoramaGraph {
+ repoId: string;
+ commit?: string;
+ commitShortHash?: string;
+ base?: string;
+ head?: string;
+ nodes: PanoramaNode[];
+ edges: PanoramaEdge[];
+ entryPoints: string[];
+ entryCatalog?: PanoramaEntryCatalogItem[];
+ layout: 'tree' | 'layered';
+ stats: {
+ nodeCount: number;
+ edgeCount: number;
+ truncated: boolean;
+ snapshotNodeCount?: number;
+ entrySurfaceCount?: number;
+ effectiveDepth?: number;
+ };
+ extractionMethod?: 'codegraph' | 'fallback';
+ pathConnected?: boolean;
+ pathMessage?: string;
+}
+
+export interface PanoramaEnrichResult {
+ labels: Record;
+ nonAuthoritative: true;
+}
+
+export type ProviderKind =
+ | 'codex-oauth'
+ | 'openai'
+ | 'openai-compatible'
+ | 'anthropic'
+ | 'ollama'
+ | 'none';
+
+export interface ModelProviderConfig {
+ kind: ProviderKind;
+ apiKey?: string;
+ baseUrl?: string;
+ model?: string;
+ oauthToken?: string;
+}
+
+export interface TraceQuestion {
+ repoId: string;
+ question: string;
+ branch?: string;
+ commitLimit?: number;
+ includeDiffEvidence?: boolean;
+}
+
+export type TraceEvidenceKind =
+ | 'commit-message'
+ | 'changed-file'
+ | 'changed-symbol'
+ | 'edge-change'
+ | 'risk-tag'
+ | 'entry-point'
+ | 'code-diff'
+ | 'delta-summary'
+ | 'delta-unavailable';
+
+export interface TraceEvidenceItem {
+ id: string;
+ kind: TraceEvidenceKind;
+ commitHash: string;
+ title: string;
+ detail: string;
+ file?: string;
+ symbol?: string;
+ score?: number;
+}
+
+export interface TraceCandidateCommit {
+ commit: CommitInfo;
+ relevanceScore: number;
+ reasons: string[];
+ matchedTerms: string[];
+ changedFiles: ChangedFile[];
+ impactSummary?: ImpactSummary;
+ deltaSummary?: DeltaSummary;
+ previousCommitHash?: string;
+}
+
+export interface TraceEvolutionState {
+ label: 'before' | 'candidate' | 'after' | 'current';
+ commitHash?: string;
+ summary: string;
+ evidenceRefs: string[];
+}
+
+export interface TraceAnswer {
+ question: string;
+ directAnswer: string;
+ directAnswerEvidenceRefs?: string[];
+ mostLikelyCommit?: CommitInfo;
+ candidates: TraceCandidateCommit[];
+ evidence: TraceEvidenceItem[];
+ impactRadius: {
+ files: string[];
+ symbols: string[];
+ entryPoints: string[];
+ riskTags: string[];
+ };
+ evolution: TraceEvolutionState[];
+ confidence: 'low' | 'medium' | 'high';
+ uncertainty: string[];
+ uncertaintyEvidenceRefs?: string[];
+ suggestedNextChecks: string[];
+ provider?: {
+ type: string;
+ model?: string;
+ used: boolean;
+ nonAuthoritativeText?: string;
+ };
+}
+
+// --- Wiki ---
+
+export type WikiSectionKind = 'overview' | 'architecture' | 'module';
+
+export interface WikiSection {
+ id: string;
+ title: string;
+ kind: WikiSectionKind;
+ area?: string;
+ files: string[];
+ symbolCount: number;
+}
+
+export interface WikiToc {
+ repoId: string;
+ commitHash: string;
+ wikiVersion: string;
+ generatedAt: string;
+ sections: WikiSection[];
+}
+
+export interface WikiCitation {
+ id: string;
+ symbol?: string;
+ file: string;
+ startLine?: number;
+ endLine?: number;
+}
+
+export interface WikiPageContent {
+ sectionId: string;
+ title: string;
+ markdown: string;
+ citations: WikiCitation[];
+ llmUsed: boolean;
+ generatedAt: string;
+}
+
+export interface WikiStatus {
+ state: 'absent' | 'generating' | 'ready' | 'error';
+ commitHash?: string;
+ totalSections?: number;
+ completedSections?: number;
+ currentSection?: string;
+ error?: string;
+ jobId?: string;
+ llmUsed?: boolean;
+ generatedAt?: string;
+}
+
+export interface WikiEvidenceItem {
+ id: string;
+ kind: 'symbol' | 'call-path' | 'file' | 'source';
+ title: string;
+ detail: string;
+ file?: string;
+ symbol?: string;
+ startLine?: number;
+ endLine?: number;
+}
+
+export interface WikiAskAnswer {
+ question: string;
+ answer: string;
+ citations: WikiCitation[];
+ evidence: WikiEvidenceItem[];
+ confidence: 'low' | 'medium' | 'high';
+ provider: {
+ type: string;
+ model?: string;
+ used: boolean;
+ nonAuthoritativeText?: string;
+ };
+}
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
new file mode 100644
index 000000000..1bc6e124b
--- /dev/null
+++ b/apps/web/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true
+ },
+ "include": ["src"]
+}
diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts
new file mode 100644
index 000000000..3179305cc
--- /dev/null
+++ b/apps/web/vite.config.ts
@@ -0,0 +1,20 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+const apiPort = Number(process.env.CODEDELTA_PORT ?? 3847);
+const uiProxied = Boolean(process.env.CODEDELTA_DEV_UI_URL?.trim());
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 5173,
+ // When the API server proxies this port on :3847, HMR must connect through the proxy.
+ hmr: uiProxied ? { clientPort: apiPort } : undefined,
+ proxy: {
+ '/api': {
+ target: `http://localhost:${apiPort}`,
+ changeOrigin: true,
+ },
+ },
+ },
+});
diff --git a/docs/codedelta/ROADMAP.md b/docs/codedelta/ROADMAP.md
new file mode 100644
index 000000000..b96108d76
--- /dev/null
+++ b/docs/codedelta/ROADMAP.md
@@ -0,0 +1,183 @@
+# CodeDelta Roadmap
+
+## Phase 1 — Foundation ✅
+
+- [x] npm workspaces and package layout
+- [x] `@codedelta/types`
+- [x] `@codedelta/repo-manager`
+- [x] `@codedelta/server` import/branches/commits APIs + registry persistence
+- [x] `@codedelta/web` import/timeline shell + settings shell
+- [x] README repositioning to CodeDelta
+
+## Phase 2 — Delta View Foundation (commit-to-commit) ✅
+
+Implemented in this phase:
+
+- [x] `CodeGraph.exportGraph()` and edge export support
+- [x] `@codedelta/snapshot-manager`
+ - safe worktree checkout (no active worktree mutation)
+ - snapshot cache key: `{repoId}/{commitHash}/{analyzerVersion}`
+ - CodeGraph primary path + TS/JS fallback extractor
+- [x] `@codedelta/graph-diff`
+ - added/removed/modified nodes
+ - added/removed edges
+ - affected nodes via BFS (`calls`, `imports`)
+- [x] `@codedelta/impact-score`
+ - deterministic scoring (files, symbols, edges, affected nodes)
+ - risk tags (`auth`, `billing`, `database`, `migration`, `env`, `config`, `api`, `routing`, `dependency`)
+- [x] Compare API
+ - `GET /api/repos/:repoId/compare?base=&head=`
+ - error mapping for repo/commit/snapshot/timeout/size/unsupported
+- [x] Delta View UI first functional version
+ - base/head selectors
+ - compare with previous commit
+ - changed files panel
+ - graph diff summary panel
+ - impact/risk panel
+ - structural node/edge lists
+- [x] Timeline integration
+ - action: **Compare with previous commit**
+
+### Notes
+
+- `POST /api/repos/:id/delta` is deprecated and now returns guidance to use `GET /compare`.
+- Rich graph rendering is intentionally deferred.
+
+
+## Phase 2.5 — Delta View UX Refinement ✅
+
+Implemented in this phase:
+
+- [x] Deterministic `DeltaSummary` generation
+ - changed files/symbols/edges/affected metrics
+ - main changed areas/modules
+ - risk breakdown with reasons
+ - suggested review order
+- [x] Impact score explanation
+ - severity labels (`low`/`medium`/`high`/`critical`)
+ - explanation reasons + top contributors
+- [x] File-level diff API
+ - `GET /api/repos/:repoId/diff?base=&head=&file=`
+ - path traversal validation + clear error mapping
+ - unified patch + parsed hunks
+- [x] Delta View UX reorganization
+ - summary-first information hierarchy
+ - tabs for files/symbols/edges/metrics
+ - changed files and symbols open file diff modal
+
+### Deferred TODOs
+
+- [ ] symbol-to-hunk precise mapping in diff viewer
+- [ ] richer graph rendering
+- [ ] LLM-assisted summary (optional mode)
+- [x] Trace View foundation
+- [x] Codex OAuth(本机 `codex login`)
+
+## Current supported DeltaSource
+
+Implemented:
+
+```ts
+type DeltaSource = {
+ type: 'commit';
+ commitHash: string;
+ label?: string;
+};
+```
+
+Not implemented yet (planned):
+
+- `branch`
+- `tag`
+- `pull_request`
+- `working_tree`
+- `folder`
+
+## Phase 3 — Trace View + Providers ✅ (foundation)
+
+- [x] `@codedelta/trace-engine` deterministic candidate retrieval + evidence assembly
+- [x] `@codedelta/provider-runtime` no-AI fallback + minimal OpenAI/OpenAI-compatible adapter
+- [x] `POST /api/repos/:id/trace` production foundation implementation
+- [x] Trace View UI first functional version
+ - [x] question input / branch / commitLimit / includeDiffEvidence
+ - [x] candidate commits with reasons and relevance score
+ - [x] evidence panel + impact radius + evolution + uncertainty
+ - [x] candidate -> Delta verification navigation (`previous -> candidate`)
+- [x] low-history fallback behavior (low confidence + uncertainty guidance)
+
+### Phase 3 deferred TODOs
+
+- [x] Codex OAuth provider(本机 CLI 凭据 + ChatGPT backend)
+- [ ] richer provider adapters (Anthropic/Ollama)
+- [ ] stronger trace confidence calibration
+- [ ] richer graph-backed evidence (deeper call-path reasoning)
+- [ ] branch/PR/working-tree/folder trace sources
+
+## Phase 3.5 — Wiki (graph-grounded docs + Ask) ✅ (foundation)
+
+- [x] Phase 0 optimizations landed first:
+ - [x] snapshot singleflight + atomic snapshot/registry/settings writes
+ - [x] compare loads base/head snapshots in parallel; trace pre-computes candidate compares with bounded concurrency
+ - [x] OpenAI/OpenAI-compatible provider timeout + retry (shared `fetchWithRetry`)
+ - [x] Delta compare double-request fix; route-level lazy loading; panorama node memoization
+- [x] `@codedelta/wiki-engine`
+ - [x] deterministic TOC planning from snapshot (overview/architecture/modules/routes/components)
+ - [x] Mermaid serialization from real edges (module graph, call flows)
+ - [x] page composition: deterministic markdown core + optional LLM narration with output validation
+ - [x] Ask: lexical scoring + graph traversal retrieval → evidence whitelist → cited answer (deterministic fallback without provider)
+- [x] `@codedelta/server`
+ - [x] generic in-memory `JobStore` for background tasks
+ - [x] `POST /wiki/generate`, `GET /wiki/status`, `GET /wiki/toc`, `GET /wiki/page`, `POST /wiki/ask`
+ - [x] cache under `.codedelta/wiki////`
+- [x] `apps/web` Wiki page: commit selector, generation progress, TOC + markdown/mermaid rendering, citations deep-linking to Panorama, Ask chat panel
+
+### Phase 3.5 deferred TODOs
+
+- [ ] wiki regeneration diff (what changed in docs between two commits)
+- [ ] embedding-free semantic re-ranking improvements for Ask retrieval
+- [ ] export wiki as static site / markdown bundle
+- [ ] streaming Ask responses
+
+## Phase 4 — Depth and polish
+
+- [ ] Deeper CodeGraph integration for snapshots (incremental indexing / reuse)
+- [x] Rich graph visualization foundation (React Flow panorama — entry exploration, Delta overlay, Trace highlight)
+- [ ] Private GitHub repository support
+- [ ] Incremental snapshot acceleration for large repositories
+- [ ] Timeline-level persisted impact scores
+- [ ] Extended DeltaSource variants beyond commits
+
+## Phase Desktop — macOS app (MVP)
+
+- [x] `@codedelta/server` static UI hosting + `CODEDELTA_MONOREPO_ROOT`
+- [x] Web boot screen (`/api/health` polling, git banner)
+- [x] `scripts/desktop-stage.mjs` — embedded Node 22+ (`node:sqlite`) + production runtime
+- [x] `apps/desktop` Tauri 2 shell (spawn/kill API, single instance, `127.0.0.1:3847`)
+- [x] Import page recent repositories
+- [x] `npm run dev:desktop` / `stage:desktop` / `build:desktop`
+- [ ] Apple code signing + notarization for public distribution
+- [x] Windows desktop (x64 NSIS installer, CI + GitHub Releases)
+- [ ] Linux desktop
+- [ ] Auto-update, menu bar quick open, drag-and-drop import
+
+## Architecture snapshot
+
+```mermaid
+flowchart LR
+ subgraph phase2 [Phase2_Delta]
+ Repo[repo_manager]
+ Snap[snapshot_manager]
+ Diff[graph_diff]
+ Impact[impact_score]
+ API[compare_api]
+ UI[delta_view_ui]
+ end
+ CG[CodeGraph_core]
+ Repo --> Snap
+ Snap --> CG
+ Snap --> Diff
+ Diff --> Impact
+ Impact --> API
+ API --> UI
+```
+
diff --git a/docs/images/codedelta-app-icon.png b/docs/images/codedelta-app-icon.png
new file mode 100644
index 000000000..f4cf6003b
Binary files /dev/null and b/docs/images/codedelta-app-icon.png differ
diff --git a/docs/images/commit-timeline-view.png b/docs/images/commit-timeline-view.png
new file mode 100644
index 000000000..804b4e6a9
Binary files /dev/null and b/docs/images/commit-timeline-view.png differ
diff --git a/docs/images/delta-file-diff-modal.png b/docs/images/delta-file-diff-modal.png
new file mode 100644
index 000000000..ff340aa3f
Binary files /dev/null and b/docs/images/delta-file-diff-modal.png differ
diff --git a/docs/images/delta-view-overview.png b/docs/images/delta-view-overview.png
new file mode 100644
index 000000000..74c69d54f
Binary files /dev/null and b/docs/images/delta-view-overview.png differ
diff --git a/docs/images/import-view.png b/docs/images/import-view.png
new file mode 100644
index 000000000..92e8b03c9
Binary files /dev/null and b/docs/images/import-view.png differ
diff --git a/docs/images/trace-view-overview.png b/docs/images/trace-view-overview.png
new file mode 100644
index 000000000..272338770
Binary files /dev/null and b/docs/images/trace-view-overview.png differ
diff --git a/package-lock.json b/package-lock.json
index e8c9d0568..36bcf4b08 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,13 +1,17 @@
{
- "name": "@colbymchenry/codegraph",
- "version": "0.9.6",
+ "name": "@codedelta/monorepo",
+ "version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "@colbymchenry/codegraph",
- "version": "0.9.6",
+ "name": "@codedelta/monorepo",
+ "version": "0.1.0",
"license": "MIT",
+ "workspaces": [
+ "packages/*",
+ "apps/*"
+ ],
"dependencies": {
"@clack/prompts": "^1.3.0",
"chokidar": "^4.0.3",
@@ -28,45 +32,49 @@
"@types/better-sqlite3": "^7.6.0",
"@types/node": "^20.19.30",
"@types/picomatch": "^4.0.2",
+ "concurrently": "^9.1.2",
"typescript": "^5.0.0",
- "vitest": "^2.1.9"
+ "vitest": "^4.1.7"
},
"engines": {
"node": ">=20.0.0 <25.0.0"
}
},
- "node_modules/@clack/core": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.3.0.tgz",
- "integrity": "sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA==",
+ "apps/desktop": {
+ "name": "@codedelta/desktop",
+ "version": "0.1.0",
"license": "MIT",
- "dependencies": {
- "fast-wrap-ansi": "^0.2.0",
- "sisteransi": "^1.0.5"
- },
- "engines": {
- "node": ">= 20.12.0"
+ "devDependencies": {
+ "@tauri-apps/cli": "^2.5.0"
}
},
- "node_modules/@clack/prompts": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.3.0.tgz",
- "integrity": "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw==",
+ "apps/web": {
+ "name": "@codedelta/web",
+ "version": "0.1.0",
"license": "MIT",
"dependencies": {
- "@clack/core": "1.3.0",
- "fast-string-width": "^3.0.2",
- "fast-wrap-ansi": "^0.2.0",
- "sisteransi": "^1.0.5"
+ "@xyflow/react": "^12.6.0",
+ "mermaid": "^11.15.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-markdown": "^10.1.0",
+ "react-router-dom": "^7.1.1",
+ "rehype-raw": "^7.0.0",
+ "rehype-sanitize": "^6.0.0",
+ "remark-gfm": "^4.0.1"
},
- "engines": {
- "node": ">= 20.12.0"
+ "devDependencies": {
+ "@types/react": "^19.0.2",
+ "@types/react-dom": "^19.0.2",
+ "@vitejs/plugin-react": "^4.3.4",
+ "typescript": "^5.0.0",
+ "vite": "^6.0.6"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
],
@@ -77,13 +85,13 @@
"aix"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
],
@@ -94,13 +102,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
],
@@ -111,13 +119,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
],
@@ -128,13 +136,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
],
@@ -145,13 +153,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
],
@@ -162,13 +170,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
],
@@ -179,13 +187,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
],
@@ -196,13 +204,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
],
@@ -213,13 +221,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
],
@@ -230,13 +238,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
],
@@ -247,13 +255,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
],
@@ -264,13 +272,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
],
@@ -281,13 +289,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
],
@@ -298,13 +306,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
],
@@ -315,13 +323,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
],
@@ -332,13 +340,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
"cpu": [
"x64"
],
@@ -349,13 +357,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
],
@@ -366,13 +374,13 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
],
@@ -383,13 +391,13 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
],
@@ -400,13 +408,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
],
@@ -417,13 +425,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
],
@@ -434,13 +442,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
],
@@ -451,188 +459,539 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "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==",
+ "apps/web/node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"dev": true,
- "license": "MIT"
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
},
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
- "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
- "cpu": [
- "arm"
- ],
+ "apps/web/node_modules/vite": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
+ "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.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 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
},
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
- "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
+ "node_modules/@antfu/install-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
+ "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
"license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
+ "dependencies": {
+ "package-manager-detector": "^1.3.0",
+ "tinyexec": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
},
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
- "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
- "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
- "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.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/@rollup/rollup-freebsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
- "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
- "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
- "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
- "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
- "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
},
- "node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
- "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
- "cpu": [
- "loong64"
- ],
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
- "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
- "cpu": [
- "loong64"
- ],
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
+ "engines": {
+ "node": ">=6.9.0"
+ }
},
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
- "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@braintree/sanitize-url": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz",
+ "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==",
+ "license": "MIT"
+ },
+ "node_modules/@chevrotain/types": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz",
+ "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@clack/core": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.3.0.tgz",
+ "integrity": "sha512-xJPHpAmEQUBrXSLx0gF+q5K/IyihXpsHZcha+jB+tyahsKRK3Dxo4D0coZDewHo12NhiuzC3dTtMPbm53GEAAA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@clack/prompts": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.3.0.tgz",
+ "integrity": "sha512-GgcWwRCs/xPtaqlMy8qRhPnZf9vlWcWZNHAitnVQ3yk7JmSralSiq5q07yaffYE8SogtDm7zFeKccx1QNVARpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@clack/core": "1.3.0",
+ "fast-string-width": "^3.0.2",
+ "fast-wrap-ansi": "^0.2.0",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 20.12.0"
+ }
+ },
+ "node_modules/@codedelta/delta-summary": {
+ "resolved": "packages/codedelta-delta-summary",
+ "link": true
+ },
+ "node_modules/@codedelta/desktop": {
+ "resolved": "apps/desktop",
+ "link": true
+ },
+ "node_modules/@codedelta/graph-diff": {
+ "resolved": "packages/codedelta-graph-diff",
+ "link": true
+ },
+ "node_modules/@codedelta/graph-subgraph": {
+ "resolved": "packages/codedelta-graph-subgraph",
+ "link": true
+ },
+ "node_modules/@codedelta/impact-score": {
+ "resolved": "packages/codedelta-impact-score",
+ "link": true
+ },
+ "node_modules/@codedelta/monorepo": {
+ "resolved": "",
+ "link": true
+ },
+ "node_modules/@codedelta/provider-runtime": {
+ "resolved": "packages/codedelta-provider-runtime",
+ "link": true
+ },
+ "node_modules/@codedelta/repo-manager": {
+ "resolved": "packages/codedelta-repo-manager",
+ "link": true
+ },
+ "node_modules/@codedelta/server": {
+ "resolved": "packages/codedelta-server",
+ "link": true
+ },
+ "node_modules/@codedelta/snapshot-manager": {
+ "resolved": "packages/codedelta-snapshot-manager",
+ "link": true
+ },
+ "node_modules/@codedelta/trace-engine": {
+ "resolved": "packages/codedelta-trace-engine",
+ "link": true
+ },
+ "node_modules/@codedelta/types": {
+ "resolved": "packages/codedelta-types",
+ "link": true
+ },
+ "node_modules/@codedelta/web": {
+ "resolved": "apps/web",
+ "link": true
+ },
+ "node_modules/@codedelta/wiki-engine": {
+ "resolved": "packages/codedelta-wiki-engine",
+ "link": true
+ },
+ "node_modules/@dagrejs/dagre": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.8.tgz",
+ "integrity": "sha512-5SEDlndt4W/LaVzPYJW+bSmSEZc9EzTf8rJ20WCKvjS5EAZAN0b+x0Yww7VMT4R3Wootkg+X9bUfUxazYw6Blw==",
+ "license": "MIT",
+ "dependencies": {
+ "@dagrejs/graphlib": "2.2.4"
+ }
+ },
+ "node_modules/@dagrejs/graphlib": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz",
+ "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">17.0.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
"cpu": [
"ppc64"
],
@@ -640,69 +999,84 @@
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
- "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
"cpu": [
- "ppc64"
+ "arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
- "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
"cpu": [
- "riscv64"
+ "arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
- "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
"cpu": [
- "riscv64"
+ "x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
- "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
"cpu": [
- "s390x"
+ "arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
"cpu": [
"x64"
],
@@ -710,27 +1084,33 @@
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
- "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
"cpu": [
- "x64"
+ "arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
- ]
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
- "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
"cpu": [
"x64"
],
@@ -738,27 +1118,33 @@
"license": "MIT",
"optional": true,
"os": [
- "openbsd"
- ]
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
- "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
"cpu": [
- "arm64"
+ "arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "openharmony"
- ]
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
- "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
"cpu": [
"arm64"
],
@@ -766,13 +1152,16 @@
"license": "MIT",
"optional": true,
"os": [
- "win32"
- ]
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
- "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
"cpu": [
"ia32"
],
@@ -780,244 +1169,2105 @@
"license": "MIT",
"optional": true,
"os": [
- "win32"
- ]
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
"cpu": [
- "x64"
+ "loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "win32"
- ]
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
- "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
"cpu": [
- "x64"
+ "mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
- "win32"
- ]
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@types/better-sqlite3": {
- "version": "7.6.13",
- "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
- "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
+ "cpu": [
+ "ppc64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@types/node": "*"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "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/node": {
- "version": "20.19.33",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
- "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
+ "cpu": [
+ "riscv64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "undici-types": "~6.21.0"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@types/picomatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz",
- "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@vitest/expect": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
- "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
+ "cpu": [
+ "s390x"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@vitest/spy": "2.1.9",
- "@vitest/utils": "2.1.9",
- "chai": "^5.1.2",
- "tinyrainbow": "^1.2.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@vitest/mocker": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
- "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@vitest/spy": "2.1.9",
- "estree-walker": "^3.0.3",
- "magic-string": "^0.30.12"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "msw": "^2.4.9",
- "vite": "^5.0.0"
- },
- "peerDependenciesMeta": {
- "msw": {
- "optional": true
- },
- "vite": {
- "optional": true
- }
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@vitest/pretty-format": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
- "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "tinyrainbow": "^1.2.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@vitest/runner": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
- "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@vitest/utils": "2.1.9",
- "pathe": "^1.1.2"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@vitest/snapshot": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
- "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@vitest/pretty-format": "2.1.9",
- "magic-string": "^0.30.12",
- "pathe": "^1.1.2"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@vitest/spy": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
- "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "tinyspy": "^3.0.2"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/@vitest/utils": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
- "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@vitest/pretty-format": "2.1.9",
- "loupe": "^3.1.2",
- "tinyrainbow": "^1.2.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "node_modules/cac": {
- "version": "6.7.14",
- "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
- "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=8"
+ "node": ">=18"
}
},
- "node_modules/chai": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
- "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
+ "cpu": [
+ "ia32"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "assertion-error": "^2.0.1",
- "check-error": "^2.1.1",
- "deep-eql": "^5.0.1",
- "loupe": "^3.1.0",
- "pathval": "^2.0.0"
- },
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
"node": ">=18"
}
},
- "node_modules/check-error": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
- "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">= 16"
+ "node": ">=18"
}
},
- "node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "node_modules/@iconify/types": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
+ "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+ "license": "MIT"
+ },
+ "node_modules/@iconify/utils": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz",
+ "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==",
"license": "MIT",
"dependencies": {
- "readdirp": "^4.0.1"
- },
- "engines": {
- "node": ">= 14.16.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
+ "@antfu/install-pkg": "^1.1.0",
+ "@iconify/types": "^2.0.0",
+ "import-meta-resolve": "^4.2.0"
+ }
+ },
+ "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/@mermaid-js/parser": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.1.tgz",
+ "integrity": "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==",
+ "license": "MIT",
+ "dependencies": {
+ "@chevrotain/types": "~11.1.1"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@paralleldrive/cuid2": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
+ "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "^1.1.5"
+ }
+ },
+ "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.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz",
+ "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz",
+ "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz",
+ "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz",
+ "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz",
+ "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz",
+ "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz",
+ "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz",
+ "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz",
+ "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz",
+ "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz",
+ "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz",
+ "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz",
+ "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz",
+ "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz",
+ "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz",
+ "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz",
+ "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz",
+ "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz",
+ "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz",
+ "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz",
+ "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz",
+ "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz",
+ "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz",
+ "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz",
+ "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==",
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tauri-apps/cli": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz",
+ "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==",
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "bin": {
+ "tauri": "tauri.js"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/tauri"
+ },
+ "optionalDependencies": {
+ "@tauri-apps/cli-darwin-arm64": "2.11.2",
+ "@tauri-apps/cli-darwin-x64": "2.11.2",
+ "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2",
+ "@tauri-apps/cli-linux-arm64-gnu": "2.11.2",
+ "@tauri-apps/cli-linux-arm64-musl": "2.11.2",
+ "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2",
+ "@tauri-apps/cli-linux-x64-gnu": "2.11.2",
+ "@tauri-apps/cli-linux-x64-musl": "2.11.2",
+ "@tauri-apps/cli-win32-arm64-msvc": "2.11.2",
+ "@tauri-apps/cli-win32-ia32-msvc": "2.11.2",
+ "@tauri-apps/cli-win32-x64-msvc": "2.11.2"
+ }
+ },
+ "node_modules/@tauri-apps/cli-darwin-arm64": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz",
+ "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-darwin-x64": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz",
+ "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz",
+ "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz",
+ "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-linux-arm64-musl": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz",
+ "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz",
+ "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-linux-x64-gnu": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz",
+ "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-linux-x64-musl": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz",
+ "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz",
+ "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz",
+ "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tauri-apps/cli-win32-x64-msvc": {
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz",
+ "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 OR MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "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/better-sqlite3": {
+ "version": "7.6.13",
+ "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
+ "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.6",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*",
+ "assertion-error": "^2.0.1"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cookiejar": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz",
+ "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.19",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+ "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/d3": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+ "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/d3-axis": "*",
+ "@types/d3-brush": "*",
+ "@types/d3-chord": "*",
+ "@types/d3-color": "*",
+ "@types/d3-contour": "*",
+ "@types/d3-delaunay": "*",
+ "@types/d3-dispatch": "*",
+ "@types/d3-drag": "*",
+ "@types/d3-dsv": "*",
+ "@types/d3-ease": "*",
+ "@types/d3-fetch": "*",
+ "@types/d3-force": "*",
+ "@types/d3-format": "*",
+ "@types/d3-geo": "*",
+ "@types/d3-hierarchy": "*",
+ "@types/d3-interpolate": "*",
+ "@types/d3-path": "*",
+ "@types/d3-polygon": "*",
+ "@types/d3-quadtree": "*",
+ "@types/d3-random": "*",
+ "@types/d3-scale": "*",
+ "@types/d3-scale-chromatic": "*",
+ "@types/d3-selection": "*",
+ "@types/d3-shape": "*",
+ "@types/d3-time": "*",
+ "@types/d3-time-format": "*",
+ "@types/d3-timer": "*",
+ "@types/d3-transition": "*",
+ "@types/d3-zoom": "*"
+ }
+ },
+ "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-axis": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+ "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-brush": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+ "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-chord": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+ "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+ "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-contour": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+ "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-array": "*",
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-dispatch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
+ "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-drag": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+ "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-dsv": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+ "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+ "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-fetch": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+ "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-dsv": "*"
+ }
+ },
+ "node_modules/@types/d3-force": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
+ "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-geo": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+ "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/geojson": "*"
+ }
+ },
+ "node_modules/@types/d3-hierarchy": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+ "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
+ "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-polygon": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+ "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-quadtree": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+ "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-random": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+ "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
+ "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-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-selection": {
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+ "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+ "license": "MIT"
+ },
+ "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-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "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/d3-transition": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+ "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/d3-zoom": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+ "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-interpolate": "*",
+ "@types/d3-selection": "*"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/express": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
+ "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^5.0.0",
+ "@types/serve-static": "^2"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
+ "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/geojson": {
+ "version": "7946.0.16",
+ "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/http-proxy": {
+ "version": "1.17.17",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.17.tgz",
+ "integrity": "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/methods": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
+ "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.33",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz",
+ "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/qs": {
+ "version": "6.15.1",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz",
+ "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.15",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz",
+ "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@types/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/superagent": {
+ "version": "8.1.10",
+ "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.10.tgz",
+ "integrity": "sha512-nbt4IWXABhW0jGmmpRzCFNlbmwCTzZ2gTUsNIr+X+ItdqPms+PAJZbWsNzpS2USqXjcoNLQcO6nXo60zcPQiIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/cookiejar": "^2.1.5",
+ "@types/methods": "^1.1.4",
+ "@types/node": "*",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/@types/supertest": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz",
+ "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/methods": "^1.1.4",
+ "@types/superagent": "^8.1.0"
+ }
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz",
+ "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==",
+ "license": "ISC"
+ },
+ "node_modules/@upsetjs/venn.js": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz",
+ "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "d3-selection": "^3.0.0",
+ "d3-transition": "^3.0.1"
+ }
+ },
+ "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/@vitest/expect": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz",
+ "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@standard-schema/spec": "^1.1.0",
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "4.1.7",
+ "@vitest/utils": "4.1.7",
+ "chai": "^6.2.2",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz",
+ "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "4.1.7",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.21"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.7.tgz",
+ "integrity": "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz",
+ "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "4.1.7",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz",
+ "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.7",
+ "@vitest/utils": "4.1.7",
+ "magic-string": "^0.30.21",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz",
+ "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.7.tgz",
+ "integrity": "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "4.1.7",
+ "convert-source-map": "^2.0.0",
+ "tinyrainbow": "^3.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@xyflow/react": {
+ "version": "12.10.2",
+ "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.10.2.tgz",
+ "integrity": "sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@xyflow/system": "0.0.76",
+ "classcat": "^5.0.3",
+ "zustand": "^4.4.0"
+ },
+ "peerDependencies": {
+ "react": ">=17",
+ "react-dom": ">=17"
+ }
+ },
+ "node_modules/@xyflow/system": {
+ "version": "0.0.76",
+ "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.76.tgz",
+ "integrity": "sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-drag": "^3.0.7",
+ "@types/d3-interpolate": "^3.0.4",
+ "@types/d3-selection": "^3.0.10",
+ "@types/d3-transition": "^3.0.8",
+ "@types/d3-zoom": "^3.0.8",
+ "d3-drag": "^3.0.0",
+ "d3-interpolate": "^3.0.1",
+ "d3-selection": "^3.0.0",
+ "d3-zoom": "^3.0.0"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.32",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz",
+ "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.5",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
+ "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.15.1",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001793",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
+ "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
+ "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/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chai": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/classcat": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
+ "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
+ "license": "MIT"
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/commander": {
@@ -1026,451 +3276,4784 @@
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
"license": "MIT",
"engines": {
- "node": ">=20"
+ "node": ">=20"
+ }
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
+ "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/concurrently": {
+ "version": "9.2.1",
+ "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz",
+ "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "4.1.2",
+ "rxjs": "7.8.2",
+ "shell-quote": "1.8.3",
+ "supports-color": "8.1.1",
+ "tree-kill": "1.2.2",
+ "yargs": "17.7.2"
+ },
+ "bin": {
+ "conc": "dist/bin/concurrently.js",
+ "concurrently": "dist/bin/concurrently.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.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/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/cookiejar": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
+ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.6",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
+ "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/cose-base": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
+ "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
+ "license": "MIT",
+ "dependencies": {
+ "layout-base": "^1.0.0"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/cytoscape": {
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.0.tgz",
+ "integrity": "sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/cytoscape-cose-bilkent": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+ "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cose-base": "^1.0.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
+ "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "cose-base": "^2.2.0"
+ },
+ "peerDependencies": {
+ "cytoscape": "^3.2.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/cose-base": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
+ "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
+ "license": "MIT",
+ "dependencies": {
+ "layout-base": "^2.0.0"
+ }
+ },
+ "node_modules/cytoscape-fcose/node_modules/layout-base": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
+ "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
+ "license": "MIT"
+ },
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "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-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/d3-dsv/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "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-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "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-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-sankey": {
+ "version": "0.12.3",
+ "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+ "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1 - 2",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-sankey/node_modules/d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-sankey/node_modules/internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+ "license": "ISC"
+ },
+ "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-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "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/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dagre-d3-es": {
+ "version": "7.0.14",
+ "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz",
+ "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==",
+ "license": "MIT",
+ "dependencies": {
+ "d3": "^7.9.0",
+ "lodash-es": "^4.17.21"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.21",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
+ "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz",
+ "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/delaunator": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz",
+ "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dezalgo": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
+ "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/dompurify": {
+ "version": "3.4.9",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.9.tgz",
+ "integrity": "sha512-4dPSRMRDqHvs0V4YDFCsaIZo4if5u0xM+llyxiM2fwuZFdKArUBAF3VtI2+n8NKg9P870WMdYk0UhqQNoWXbfQ==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.362",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.362.tgz",
+ "integrity": "sha512-PUY2DrLvkjkUuWqq+KPL2iWshrJsZOcIojzRQ7eXFacc9dWga7MGMJAa15VbiejSZB1PAXaRLAiKgruHP8LB1w==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz",
+ "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-toolkit": {
+ "version": "1.47.0",
+ "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.47.0.tgz",
+ "integrity": "sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==",
+ "license": "MIT",
+ "workspaces": [
+ "docs",
+ "benchmarks"
+ ]
+ },
+ "node_modules/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
+ }
+ },
+ "node_modules/esbuild/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/esbuild/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "license": "MIT"
+ },
+ "node_modules/expect-type": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
+ "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
+ "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.5",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.15.1",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-safe-stringify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-string-truncated-width": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
+ "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-string-width": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
+ "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-truncated-width": "^3.0.2"
+ }
+ },
+ "node_modules/fast-wrap-ansi": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz",
+ "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-string-width": "^3.0.2"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/formidable": {
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz",
+ "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@paralleldrive/cuid2": "^2.2.2",
+ "dezalgo": "^1.0.4",
+ "once": "^1.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "url": "https://ko-fi.com/tunnckoCore/commissions"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hachure-fill": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
+ "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-raw": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
+ "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "hast-util-to-parse5": "^8.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "parse5": "^7.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-sanitize": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz",
+ "integrity": "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "unist-util-position": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz",
+ "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/http-proxy-middleware": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.6.tgz",
+ "integrity": "sha512-jhO3QfahaHWfQjEnyGW0vpYIYaXcnA6FEfehrBthOokGppvmI6zcV+1yb6TWn3vyeh8yQUoEqH51DNHOCjivxg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-proxy": "^1.17.15",
+ "debug": "^4.3.6",
+ "http-proxy": "^1.18.1",
+ "is-glob": "^4.0.3",
+ "is-plain-object": "^5.0.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": "^14.18.0 || ^16.10.0 || >=18.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz",
+ "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+ "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+ "license": "MIT"
+ },
+ "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/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "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/jsonc-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "license": "MIT"
+ },
+ "node_modules/katex": {
+ "version": "0.16.47",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.47.tgz",
+ "integrity": "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^8.3.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/katex/node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/khroma": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
+ "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
+ },
+ "node_modules/layout-base": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
+ "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash-es": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
+ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
+ "license": "MIT"
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "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/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/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/marked": {
+ "version": "16.4.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
+ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz",
+ "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mermaid": {
+ "version": "11.15.0",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.15.0.tgz",
+ "integrity": "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@braintree/sanitize-url": "^7.1.1",
+ "@iconify/utils": "^3.0.2",
+ "@mermaid-js/parser": "^1.1.1",
+ "@types/d3": "^7.4.3",
+ "@upsetjs/venn.js": "^2.0.0",
+ "cytoscape": "^3.33.1",
+ "cytoscape-cose-bilkent": "^4.1.0",
+ "cytoscape-fcose": "^2.2.0",
+ "d3": "^7.9.0",
+ "d3-sankey": "^0.12.3",
+ "dagre-d3-es": "7.0.14",
+ "dayjs": "^1.11.19",
+ "dompurify": "^3.3.1",
+ "es-toolkit": "^1.45.1",
+ "katex": "^0.16.25",
+ "khroma": "^2.1.0",
+ "marked": "^16.3.0",
+ "roughjs": "^4.6.6",
+ "stylis": "^4.3.6",
+ "ts-dedent": "^2.2.0",
+ "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "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/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.46",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz",
+ "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obug": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
+ "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
+ "dev": true,
+ "funding": [
+ "https://github.com/sponsors/sxzz",
+ "https://opencollective.com/debug"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/package-manager-detector": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz",
+ "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==",
+ "license": "MIT"
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-data-parser": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
+ "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
+ "license": "MIT"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "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/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/points-on-curve": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
+ "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",
+ "license": "MIT"
+ },
+ "node_modules/points-on-path": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",
+ "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",
+ "license": "MIT",
+ "dependencies": {
+ "path-data-parser": "0.1.0",
+ "points-on-curve": "0.2.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "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.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz",
+ "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
+ "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
+ "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.6",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
+ "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.6"
+ }
+ },
+ "node_modules/react-markdown": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
+ "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": "7.15.1",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz",
+ "integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.15.1",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.1.tgz",
+ "integrity": "sha512-AzF62gjY6U9rkMq4RfP/r2EVtQ7DMfNMjyOp/flLTCrtRylLiK4wT4pSq6O8rOXZ2eXdZYJPEYe+ifomiv+Igg==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.15.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
+ "node_modules/react-router/node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+ "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-raw": "^9.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-sanitize": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-sanitize/-/rehype-sanitize-6.0.0.tgz",
+ "integrity": "sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-sanitize": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "license": "MIT"
+ },
+ "node_modules/robust-predicates": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz",
+ "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==",
+ "license": "Unlicense"
+ },
+ "node_modules/rollup": {
+ "version": "4.60.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz",
+ "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==",
+ "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.4",
+ "@rollup/rollup-android-arm64": "4.60.4",
+ "@rollup/rollup-darwin-arm64": "4.60.4",
+ "@rollup/rollup-darwin-x64": "4.60.4",
+ "@rollup/rollup-freebsd-arm64": "4.60.4",
+ "@rollup/rollup-freebsd-x64": "4.60.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.60.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.60.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.60.4",
+ "@rollup/rollup-linux-arm64-musl": "4.60.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.60.4",
+ "@rollup/rollup-linux-loong64-musl": "4.60.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.60.4",
+ "@rollup/rollup-linux-ppc64-musl": "4.60.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.60.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.60.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.60.4",
+ "@rollup/rollup-linux-x64-gnu": "4.60.4",
+ "@rollup/rollup-linux-x64-musl": "4.60.4",
+ "@rollup/rollup-openbsd-x64": "4.60.4",
+ "@rollup/rollup-openharmony-arm64": "4.60.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.60.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.60.4",
+ "@rollup/rollup-win32-x64-gnu": "4.60.4",
+ "@rollup/rollup-win32-x64-msvc": "4.60.4",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/roughjs": {
+ "version": "4.6.6",
+ "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",
+ "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",
+ "license": "MIT",
+ "dependencies": {
+ "hachure-fill": "^0.5.2",
+ "path-data-parser": "^0.1.0",
+ "points-on-curve": "^0.2.0",
+ "points-on-path": "^0.2.1"
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "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/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/shell-quote": {
+ "version": "1.8.3",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
+ "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "license": "MIT"
+ },
+ "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/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/std-env": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
+ "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
+ "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.14"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.14",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz",
+ "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.7"
+ }
+ },
+ "node_modules/stylis": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz",
+ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==",
+ "license": "MIT"
+ },
+ "node_modules/superagent": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz",
+ "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "component-emitter": "^1.3.1",
+ "cookiejar": "^2.1.4",
+ "debug": "^4.3.7",
+ "fast-safe-stringify": "^2.1.1",
+ "form-data": "^4.0.5",
+ "formidable": "^3.5.4",
+ "methods": "^1.1.2",
+ "mime": "2.6.0",
+ "qs": "^6.14.1"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
+ "node_modules/superagent/node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/supertest": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz",
+ "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cookie-signature": "^1.2.2",
+ "methods": "^1.1.2",
+ "superagent": "^10.3.0"
+ },
+ "engines": {
+ "node": ">=14.18.0"
+ }
+ },
+ "node_modules/supertest/node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.3.tgz",
+ "integrity": "sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.16",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
+ "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/tree-sitter-wasms": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/tree-sitter-wasms/-/tree-sitter-wasms-0.1.13.tgz",
+ "integrity": "sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==",
+ "license": "Unlicense",
+ "dependencies": {
+ "tree-sitter-wasms": "^0.1.11"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/ts-dedent": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.3.0.tgz",
+ "integrity": "sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.10"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tsx": {
+ "version": "4.22.3",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz",
+ "integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.28.0"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
+ "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
+ "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
+ "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
+ "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
+ "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
+ "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
+ "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
+ "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
+ "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
+ "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
+ "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
+ "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
+ "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
+ "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
+ "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
+ "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
+ "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
+ "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
+ "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
"engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
+ "node": ">=18"
}
},
- "node_modules/deep-eql": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
- "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
+ "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
"engines": {
- "node": ">=6"
+ "node": ">=18"
}
},
- "node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
- "dev": true,
- "license": "MIT"
- },
- "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==",
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
+ "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "hasInstallScript": true,
"license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
"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": ">=18"
}
},
- "node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
+ "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0"
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/expect-type": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
- "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
+ "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "Apache-2.0",
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
"engines": {
- "node": ">=12.0.0"
+ "node": ">=18"
}
},
- "node_modules/fast-string-truncated-width": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
- "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
- "license": "MIT"
- },
- "node_modules/fast-string-width": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
- "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
+ "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "fast-string-truncated-width": "^3.0.2"
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/fast-wrap-ansi": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz",
- "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==",
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
+ "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "fast-string-width": "^3.0.2"
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
+ "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
- "darwin"
+ "win32"
],
"engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ "node": ">=18"
}
},
- "node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.28.0",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
+ "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
+ "dev": true,
+ "hasInstallScript": true,
"license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
"engines": {
- "node": ">= 4"
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.28.0",
+ "@esbuild/android-arm": "0.28.0",
+ "@esbuild/android-arm64": "0.28.0",
+ "@esbuild/android-x64": "0.28.0",
+ "@esbuild/darwin-arm64": "0.28.0",
+ "@esbuild/darwin-x64": "0.28.0",
+ "@esbuild/freebsd-arm64": "0.28.0",
+ "@esbuild/freebsd-x64": "0.28.0",
+ "@esbuild/linux-arm": "0.28.0",
+ "@esbuild/linux-arm64": "0.28.0",
+ "@esbuild/linux-ia32": "0.28.0",
+ "@esbuild/linux-loong64": "0.28.0",
+ "@esbuild/linux-mips64el": "0.28.0",
+ "@esbuild/linux-ppc64": "0.28.0",
+ "@esbuild/linux-riscv64": "0.28.0",
+ "@esbuild/linux-s390x": "0.28.0",
+ "@esbuild/linux-x64": "0.28.0",
+ "@esbuild/netbsd-arm64": "0.28.0",
+ "@esbuild/netbsd-x64": "0.28.0",
+ "@esbuild/openbsd-arm64": "0.28.0",
+ "@esbuild/openbsd-x64": "0.28.0",
+ "@esbuild/openharmony-arm64": "0.28.0",
+ "@esbuild/sunos-x64": "0.28.0",
+ "@esbuild/win32-arm64": "0.28.0",
+ "@esbuild/win32-ia32": "0.28.0",
+ "@esbuild/win32-x64": "0.28.0"
}
},
- "node_modules/jsonc-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
- "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
- "license": "MIT"
- },
- "node_modules/loupe": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
- "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
- "dev": true,
- "license": "MIT"
- },
- "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,
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"license": "MIT",
"dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
}
},
- "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==",
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
+ "license": "Apache-2.0",
"bin": {
- "nanoid": "bin/nanoid.cjs"
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
},
"engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ "node": ">=14.17"
}
},
- "node_modules/pathe": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
- "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
- "dev": true,
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
- "node_modules/pathval": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
- "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
- "dev": true,
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
"license": "MIT",
- "engines": {
- "node": ">= 14.16"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "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/unist-util-is": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
+ "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
- "node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
"license": "MIT",
- "engines": {
- "node": ">=12"
+ "dependencies": {
+ "@types/unist": "^3.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "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"
- }
- ],
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.11",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
+ "@types/unist": "^3.0.0"
},
- "engines": {
- "node": "^10 || ^12 || >=14"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/readdirp": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
- "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "node_modules/unist-util-visit": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz",
+ "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==",
"license": "MIT",
- "engines": {
- "node": ">= 14.18.0"
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
},
"funding": {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+ "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
}
},
- "node_modules/rollup": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
- "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
+ "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": {
- "@types/estree": "1.0.8"
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
},
"bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
+ "update-browserslist-db": "cli.js"
},
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.57.1",
- "@rollup/rollup-android-arm64": "4.57.1",
- "@rollup/rollup-darwin-arm64": "4.57.1",
- "@rollup/rollup-darwin-x64": "4.57.1",
- "@rollup/rollup-freebsd-arm64": "4.57.1",
- "@rollup/rollup-freebsd-x64": "4.57.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.57.1",
- "@rollup/rollup-linux-arm64-gnu": "4.57.1",
- "@rollup/rollup-linux-arm64-musl": "4.57.1",
- "@rollup/rollup-linux-loong64-gnu": "4.57.1",
- "@rollup/rollup-linux-loong64-musl": "4.57.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.57.1",
- "@rollup/rollup-linux-ppc64-musl": "4.57.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.57.1",
- "@rollup/rollup-linux-riscv64-musl": "4.57.1",
- "@rollup/rollup-linux-s390x-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-musl": "4.57.1",
- "@rollup/rollup-openbsd-x64": "4.57.1",
- "@rollup/rollup-openharmony-arm64": "4.57.1",
- "@rollup/rollup-win32-arm64-msvc": "4.57.1",
- "@rollup/rollup-win32-ia32-msvc": "4.57.1",
- "@rollup/rollup-win32-x64-gnu": "4.57.1",
- "@rollup/rollup-win32-x64-msvc": "4.57.1",
- "fsevents": "~2.3.2"
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
}
},
- "node_modules/siginfo": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
- "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
- "license": "MIT"
- },
- "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/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/stackback": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
- "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/std-env": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
- "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinybench": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
- "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinyexec": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
- "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinypool": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
- "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
- "dev": true,
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": ">= 0.4.0"
}
},
- "node_modules/tinyrainbow": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
- "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
- "dev": true,
+ "node_modules/uuid": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz",
+ "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
"license": "MIT",
- "engines": {
- "node": ">=14.0.0"
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
}
},
- "node_modules/tinyspy": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
- "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
- "dev": true,
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
- "node": ">=14.0.0"
+ "node": ">= 0.8"
}
},
- "node_modules/tree-sitter-wasms": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/tree-sitter-wasms/-/tree-sitter-wasms-0.1.13.tgz",
- "integrity": "sha512-wT+cR6DwaIz80/vho3AvSF0N4txuNx/5bcRKoXouOfClpxh/qqrF4URNLQXbbt8MaAxeksZcZd1j8gcGjc+QxQ==",
- "license": "Unlicense",
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
"dependencies": {
- "tree-sitter-wasms": "^0.1.11"
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "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"
+ "node_modules/vfile-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
},
- "engines": {
- "node": ">=14.17"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
}
},
- "node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
- "dev": true,
- "license": "MIT"
+ "node_modules/vfile-message": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
+ "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
},
"node_modules/vite": {
- "version": "5.4.21",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
- "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz",
+ "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -1479,19 +8062,25 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
+ "jiti": {
+ "optional": true
+ },
"less": {
"optional": true
},
@@ -1512,85 +8101,89 @@
},
"terser": {
"optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
}
}
},
- "node_modules/vite-node": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
- "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cac": "^6.7.14",
- "debug": "^4.3.7",
- "es-module-lexer": "^1.5.4",
- "pathe": "^1.1.2",
- "vite": "^5.0.0"
- },
- "bin": {
- "vite-node": "vite-node.mjs"
- },
- "engines": {
- "node": "^18.0.0 || >=20.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
"node_modules/vitest": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
- "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz",
+ "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vitest/expect": "2.1.9",
- "@vitest/mocker": "2.1.9",
- "@vitest/pretty-format": "^2.1.9",
- "@vitest/runner": "2.1.9",
- "@vitest/snapshot": "2.1.9",
- "@vitest/spy": "2.1.9",
- "@vitest/utils": "2.1.9",
- "chai": "^5.1.2",
- "debug": "^4.3.7",
- "expect-type": "^1.1.0",
- "magic-string": "^0.30.12",
- "pathe": "^1.1.2",
- "std-env": "^3.8.0",
+ "@vitest/expect": "4.1.7",
+ "@vitest/mocker": "4.1.7",
+ "@vitest/pretty-format": "4.1.7",
+ "@vitest/runner": "4.1.7",
+ "@vitest/snapshot": "4.1.7",
+ "@vitest/spy": "4.1.7",
+ "@vitest/utils": "4.1.7",
+ "es-module-lexer": "^2.0.0",
+ "expect-type": "^1.3.0",
+ "magic-string": "^0.30.21",
+ "obug": "^2.1.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "std-env": "^4.0.0-rc.1",
"tinybench": "^2.9.0",
- "tinyexec": "^0.3.1",
- "tinypool": "^1.0.1",
- "tinyrainbow": "^1.2.0",
- "vite": "^5.0.0",
- "vite-node": "2.1.9",
+ "tinyexec": "^1.0.2",
+ "tinyglobby": "^0.2.15",
+ "tinyrainbow": "^3.1.0",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
"why-is-node-running": "^2.3.0"
},
"bin": {
"vitest": "vitest.mjs"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
},
"funding": {
"url": "https://opencollective.com/vitest"
},
"peerDependencies": {
"@edge-runtime/vm": "*",
- "@types/node": "^18.0.0 || >=20.0.0",
- "@vitest/browser": "2.1.9",
- "@vitest/ui": "2.1.9",
+ "@opentelemetry/api": "^1.9.0",
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
+ "@vitest/browser-playwright": "4.1.7",
+ "@vitest/browser-preview": "4.1.7",
+ "@vitest/browser-webdriverio": "4.1.7",
+ "@vitest/coverage-istanbul": "4.1.7",
+ "@vitest/coverage-v8": "4.1.7",
+ "@vitest/ui": "4.1.7",
"happy-dom": "*",
- "jsdom": "*"
+ "jsdom": "*",
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@edge-runtime/vm": {
"optional": true
},
+ "@opentelemetry/api": {
+ "optional": true
+ },
"@types/node": {
"optional": true
},
- "@vitest/browser": {
+ "@vitest/browser-playwright": {
+ "optional": true
+ },
+ "@vitest/browser-preview": {
+ "optional": true
+ },
+ "@vitest/browser-webdriverio": {
+ "optional": true
+ },
+ "@vitest/coverage-istanbul": {
+ "optional": true
+ },
+ "@vitest/coverage-v8": {
"optional": true
},
"@vitest/ui": {
@@ -1601,9 +8194,22 @@
},
"jsdom": {
"optional": true
+ },
+ "vite": {
+ "optional": false
}
}
},
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
"node_modules/web-tree-sitter": {
"version": "0.25.10",
"resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.25.10.tgz",
@@ -1634,6 +8240,226 @@
"engines": {
"node": ">=8"
}
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "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/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
+ "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
+ "license": "MIT",
+ "dependencies": {
+ "use-sync-external-store": "^1.2.2"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0.6",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "packages/codedelta-delta-summary": {
+ "name": "@codedelta/delta-summary",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-graph-diff": {
+ "name": "@codedelta/graph-diff",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-graph-subgraph": {
+ "name": "@codedelta/graph-subgraph",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/graph-diff": "*",
+ "@codedelta/types": "*",
+ "@dagrejs/dagre": "^1.1.4"
+ }
+ },
+ "packages/codedelta-impact-score": {
+ "name": "@codedelta/impact-score",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/graph-diff": "*",
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-provider-runtime": {
+ "name": "@codedelta/provider-runtime",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-repo-manager": {
+ "name": "@codedelta/repo-manager",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-server": {
+ "name": "@codedelta/server",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/delta-summary": "*",
+ "@codedelta/graph-diff": "*",
+ "@codedelta/graph-subgraph": "*",
+ "@codedelta/impact-score": "*",
+ "@codedelta/provider-runtime": "*",
+ "@codedelta/repo-manager": "*",
+ "@codedelta/snapshot-manager": "*",
+ "@codedelta/trace-engine": "*",
+ "@codedelta/types": "*",
+ "@codedelta/wiki-engine": "*",
+ "cors": "^2.8.5",
+ "express": "^4.21.2",
+ "http-proxy-middleware": "^3.0.6"
+ },
+ "devDependencies": {
+ "@types/cors": "^2.8.17",
+ "@types/express": "^5.0.0",
+ "@types/supertest": "^6.0.2",
+ "supertest": "^7.0.0",
+ "tsx": "^4.19.2"
+ }
+ },
+ "packages/codedelta-snapshot-manager": {
+ "name": "@codedelta/snapshot-manager",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/monorepo": "file:../../",
+ "@codedelta/repo-manager": "*",
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-trace-engine": {
+ "name": "@codedelta/trace-engine",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/provider-runtime": "*",
+ "@codedelta/types": "*"
+ }
+ },
+ "packages/codedelta-types": {
+ "name": "@codedelta/types",
+ "version": "0.1.0",
+ "license": "MIT"
+ },
+ "packages/codedelta-wiki-engine": {
+ "name": "@codedelta/wiki-engine",
+ "version": "0.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@codedelta/graph-subgraph": "*",
+ "@codedelta/types": "*"
+ }
}
}
}
diff --git a/package.json b/package.json
index a02891415..ab00a9f4f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,12 @@
{
- "name": "@colbymchenry/codegraph",
- "version": "0.9.6",
- "description": "Supercharge Claude Code with semantic code intelligence. 94% fewer tool calls • 77% faster exploration • 100% local.",
+ "name": "@codedelta/monorepo",
+ "version": "0.1.0",
+ "description": "CodeDelta — local-first commit-aware structural code intelligence, built on CodeGraph.",
+ "private": true,
+ "workspaces": [
+ "packages/*",
+ "apps/*"
+ ],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
@@ -14,9 +19,14 @@
],
"scripts": {
"build": "tsc && npm run copy-assets && node -e \"require('fs').chmodSync('dist/bin/codegraph.js', 0o755)\"",
+ "build:codedelta": "npm run build && npm run build -w @codedelta/types && npm run build --workspaces --if-present",
"preuninstall": "node dist/bin/uninstall.js",
"copy-assets": "node -e \"const fs=require('fs');fs.mkdirSync('dist/db',{recursive:true});fs.copyFileSync('src/db/schema.sql','dist/db/schema.sql');fs.mkdirSync('dist/extraction/wasm',{recursive:true});fs.readdirSync('src/extraction/wasm').filter(f=>f.endsWith('.wasm')).forEach(f=>fs.copyFileSync('src/extraction/wasm/'+f,'dist/extraction/wasm/'+f))\"",
"dev": "tsc --watch",
+ "dev:codedelta": "npm run build:codedelta && CODEDELTA_PORT=3847 CODEDELTA_DEV_UI_URL=http://localhost:5173 concurrently -n server,web,provider -c blue,green,magenta \"npm run dev -w @codedelta/server\" \"npm run dev -w @codedelta/web\" \"npm run dev -w @codedelta/provider-runtime\"",
+ "stage:desktop": "node scripts/desktop-stage.mjs",
+ "dev:desktop": "CODEDELTA_PORT=3847 CODEDELTA_DEV_UI_URL=http://localhost:5173 concurrently -n server,web,tauri -c blue,green,cyan \"npm run dev -w @codedelta/server\" \"npm run dev -w @codedelta/web\" \"npm run dev -w @codedelta/desktop\"",
+ "build:desktop": "npm run stage:desktop && npm run build:app -w @codedelta/desktop",
"cli": "npm run build && node dist/bin/codegraph.js",
"test": "vitest run",
"test:watch": "vitest",
@@ -48,8 +58,9 @@
"@types/better-sqlite3": "^7.6.0",
"@types/node": "^20.19.30",
"@types/picomatch": "^4.0.2",
+ "concurrently": "^9.1.2",
"typescript": "^5.0.0",
- "vitest": "^2.1.9"
+ "vitest": "^4.1.7"
},
"engines": {
"node": ">=20.0.0 <25.0.0"
diff --git a/packages/codedelta-delta-summary/__tests__/delta-summary.test.ts b/packages/codedelta-delta-summary/__tests__/delta-summary.test.ts
new file mode 100644
index 000000000..a63953cc6
--- /dev/null
+++ b/packages/codedelta-delta-summary/__tests__/delta-summary.test.ts
@@ -0,0 +1,54 @@
+import { describe, expect, it } from 'vitest';
+import type { GraphDiff, ImpactSummary } from '@codedelta/types';
+import { buildDeltaSummary } from '../src';
+
+const diff: GraphDiff = {
+ baseCommit: 'a',
+ headCommit: 'b',
+ addedNodes: [
+ {
+ id: 'src/auth/login.ts::login',
+ kind: 'function',
+ name: 'login',
+ qualifiedName: 'src/auth/login.ts::login',
+ filePath: 'src/auth/login.ts',
+ language: 'typescript',
+ startLine: 1,
+ endLine: 20,
+ },
+ ],
+ removedNodes: [],
+ modifiedNodes: [],
+ addedEdges: [],
+ removedEdges: [],
+ affectedNodeIds: ['src/auth/login.ts::login'],
+ changedFiles: [{ path: 'src/auth/login.ts', status: 'modified' }],
+ summary: {
+ symbolsAdded: 1,
+ symbolsRemoved: 0,
+ symbolsModified: 0,
+ edgesAdded: 0,
+ edgesRemoved: 0,
+ },
+};
+
+const impact: ImpactSummary = {
+ commitHash: 'b',
+ score: 30,
+ changedSymbols: 1,
+ changedEdges: 0,
+ affectedModules: ['src/auth'],
+ impactedEntryPoints: [],
+ riskTags: ['auth'],
+};
+
+describe('buildDeltaSummary', () => {
+ it('builds deterministic summary sections', () => {
+ const summary = buildDeltaSummary(diff, impact);
+ expect(summary.metrics.changedFiles).toBe(1);
+ expect(summary.metrics.changedSymbols).toBe(1);
+ expect(summary.mainAreas[0]?.name).toBe('src/auth');
+ expect(summary.risks[0]?.tag).toBe('auth');
+ expect(summary.reviewOrder[0]?.file).toBe('src/auth/login.ts');
+ });
+});
diff --git a/packages/codedelta-delta-summary/package.json b/packages/codedelta-delta-summary/package.json
new file mode 100644
index 000000000..539ab6b74
--- /dev/null
+++ b/packages/codedelta-delta-summary/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "@codedelta/delta-summary",
+ "version": "0.1.0",
+ "description": "Deterministic Delta Summary generator for CodeDelta",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/types": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-delta-summary/src/index.ts b/packages/codedelta-delta-summary/src/index.ts
new file mode 100644
index 000000000..18747f5f3
--- /dev/null
+++ b/packages/codedelta-delta-summary/src/index.ts
@@ -0,0 +1,144 @@
+import type { DeltaSummary, GraphDiff, ImpactSummary } from '@codedelta/types';
+
+function moduleName(filePath: string): string {
+ const p = filePath.split('/').filter(Boolean);
+ if (p.length >= 2) return `${p[0]}/${p[1]}`;
+ return p[0] ?? filePath;
+}
+
+function areaForFile(filePath: string): string {
+ if (filePath.startsWith('src/')) return moduleName(filePath);
+ return filePath.split('/')[0] ?? filePath;
+}
+
+function riskReason(tag: string): string {
+ switch (tag) {
+ case 'auth':
+ return 'Authentication/session related files changed';
+ case 'billing':
+ return 'Billing/payment related files changed';
+ case 'database':
+ return 'Data/storage boundary changed';
+ case 'migration':
+ return 'Migration-like changes detected';
+ case 'env':
+ return 'Runtime environment settings touched';
+ case 'config':
+ return 'Global configuration touched';
+ case 'api':
+ return 'API surface files changed';
+ case 'routing':
+ return 'Route or navigation behavior touched';
+ case 'dependency':
+ return 'Dependency/import graph touched';
+ default:
+ return 'Potentially sensitive area changed';
+ }
+}
+
+function priorityFrom(file: string, riskTags: string[]): 'high' | 'medium' | 'low' {
+ if (riskTags.length > 0) return 'high';
+ if (/config|auth|billing|api|route|Cargo\.toml|package\.json/i.test(file)) return 'high';
+ if (/service|controller|handler|core/i.test(file)) return 'medium';
+ return 'low';
+}
+
+/**
+ * Deterministic human-readable summary from compare output.
+ */
+export function buildDeltaSummary(diff: GraphDiff, impact: ImpactSummary): DeltaSummary {
+ const changedSymbols =
+ diff.summary.symbolsAdded + diff.summary.symbolsRemoved + diff.summary.symbolsModified;
+ const edgeChanges = diff.summary.edgesAdded + diff.summary.edgesRemoved;
+
+ const fileToSymbolCount = new Map();
+ for (const n of diff.addedNodes) {
+ fileToSymbolCount.set(n.filePath, (fileToSymbolCount.get(n.filePath) ?? 0) + 1);
+ }
+ for (const n of diff.removedNodes) {
+ fileToSymbolCount.set(n.filePath, (fileToSymbolCount.get(n.filePath) ?? 0) + 1);
+ }
+ for (const m of diff.modifiedNodes) {
+ const file = m.after.filePath;
+ fileToSymbolCount.set(file, (fileToSymbolCount.get(file) ?? 0) + 1);
+ }
+
+ const areaMap = new Map; changedSymbols: number; riskTags: Set }>();
+ for (const f of diff.changedFiles) {
+ const area = areaForFile(f.path);
+ const record = areaMap.get(area) ?? { files: new Set(), changedSymbols: 0, riskTags: new Set() };
+ record.files.add(f.path);
+ record.changedSymbols += fileToSymbolCount.get(f.path) ?? 0;
+ for (const tag of impact.riskTags) {
+ if (new RegExp(tag, 'i').test(f.path)) {
+ record.riskTags.add(tag);
+ }
+ }
+ areaMap.set(area, record);
+ }
+
+ const mainAreas = Array.from(areaMap.entries())
+ .map(([name, rec]) => ({
+ name,
+ files: Array.from(rec.files).sort().slice(0, 8),
+ changedSymbols: rec.changedSymbols,
+ riskTags: Array.from(rec.riskTags).sort(),
+ }))
+ .sort((a, b) => b.changedSymbols - a.changedSymbols)
+ .slice(0, 8);
+
+ const risks = impact.riskTags.map((tag) => {
+ const files = diff.changedFiles
+ .map((f) => f.path)
+ .filter((f) => new RegExp(tag, 'i').test(f))
+ .slice(0, 10);
+ return {
+ tag,
+ reason: riskReason(tag),
+ files,
+ };
+ });
+
+ const reviewOrder = diff.changedFiles
+ .map((f) => ({
+ file: f.path,
+ reason:
+ f.status === 'deleted'
+ ? 'File deletion may break downstream dependencies'
+ : f.status === 'renamed'
+ ? 'Rename can break imports and routes'
+ : 'Changed in commit range',
+ priority: priorityFrom(
+ f.path,
+ impact.riskTags.filter((tag) => new RegExp(tag, 'i').test(f.path)),
+ ),
+ score:
+ (fileToSymbolCount.get(f.path) ?? 0) +
+ (impact.riskTags.some((tag) => new RegExp(tag, 'i').test(f.path)) ? 10 : 0),
+ }))
+ .sort((a, b) => b.score - a.score)
+ .slice(0, 15)
+ .map(({ file, reason, priority }) => ({ file, reason, priority }));
+
+ return {
+ title: 'Commit-to-commit structural delta',
+ overview: [
+ `${diff.changedFiles.length} changed files`,
+ `${changedSymbols} changed symbols`,
+ `${edgeChanges} dependency edge changes`,
+ `${diff.affectedNodeIds.length} affected nodes`,
+ impact.impactedEntryPoints.length > 0
+ ? `${impact.impactedEntryPoints.length} affected entry points`
+ : 'No impacted entry points detected',
+ ],
+ mainAreas,
+ risks,
+ reviewOrder,
+ metrics: {
+ changedFiles: diff.changedFiles.length,
+ changedSymbols,
+ edgeChanges,
+ affectedNodes: diff.affectedNodeIds.length,
+ },
+ };
+}
diff --git a/packages/codedelta-delta-summary/tsconfig.json b/packages/codedelta-delta-summary/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-delta-summary/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-graph-diff/__tests__/graph-diff.test.ts b/packages/codedelta-graph-diff/__tests__/graph-diff.test.ts
new file mode 100644
index 000000000..fde5235a1
--- /dev/null
+++ b/packages/codedelta-graph-diff/__tests__/graph-diff.test.ts
@@ -0,0 +1,86 @@
+import { describe, expect, it } from 'vitest';
+import type { CodeGraphSnapshot } from '@codedelta/types';
+import { computeGraphDiff } from '../src';
+
+function snap(
+ commitHash: string,
+ nodes: CodeGraphSnapshot['nodes'],
+ edges: CodeGraphSnapshot['edges'] = [],
+): CodeGraphSnapshot {
+ return {
+ repoId: 'test',
+ commitHash,
+ analyzerVersion: '0.1.0',
+ createdAt: new Date().toISOString(),
+ nodeCount: nodes.length,
+ edgeCount: edges.length,
+ nodes,
+ edges,
+ files: [...new Set(nodes.map((n) => n.filePath))],
+ };
+}
+
+function node(
+ id: string,
+ overrides: Partial = {},
+): CodeGraphSnapshot['nodes'][0] {
+ return {
+ id,
+ kind: 'function',
+ name: id.split('::').pop() ?? id,
+ qualifiedName: id,
+ filePath: id.split('::')[0] ?? 'src/a.ts',
+ language: 'typescript',
+ startLine: 1,
+ endLine: 10,
+ ...overrides,
+ };
+}
+
+describe('computeGraphDiff', () => {
+ it('detects added and removed nodes', () => {
+ const base = snap('base', [node('src/a.ts::foo')]);
+ const head = snap('head', [node('src/a.ts::bar')]);
+ const diff = computeGraphDiff({ base, head });
+ expect(diff.addedNodes).toHaveLength(1);
+ expect(diff.removedNodes).toHaveLength(1);
+ expect(diff.addedNodes[0]?.id).toBe('src/a.ts::bar');
+ });
+
+ it('treats same semantic id with line change as modified', () => {
+ const base = snap('base', [node('src/a.ts::foo', { startLine: 1, endLine: 5 })]);
+ const head = snap('head', [node('src/a.ts::foo', { startLine: 1, endLine: 12 })]);
+ const diff = computeGraphDiff({ base, head });
+ expect(diff.modifiedNodes).toHaveLength(1);
+ expect(diff.removedNodes).toHaveLength(0);
+ expect(diff.addedNodes).toHaveLength(0);
+ expect(diff.modifiedNodes[0]?.changes).toContain('endLine');
+ });
+
+ it('detects edge changes', () => {
+ const n1 = node('src/a.ts::a');
+ const n2 = node('src/a.ts::b');
+ const base = snap('base', [n1, n2], []);
+ const head = snap('head', [n1, n2], [{ source: n1.id, target: n2.id, kind: 'calls' }]);
+ const diff = computeGraphDiff({ base, head });
+ expect(diff.addedEdges).toHaveLength(1);
+ expect(diff.summary.edgesAdded).toBe(1);
+ });
+
+ it('computes affected nodes via BFS', () => {
+ const a = node('src/a.ts::a');
+ const b = node('src/a.ts::b');
+ const c = node('src/a.ts::c');
+ const base = snap('base', [a, b, c], [
+ { source: b.id, target: c.id, kind: 'calls' },
+ ]);
+ const head = snap('head', [a, b, c], [
+ { source: a.id, target: b.id, kind: 'calls' },
+ { source: b.id, target: c.id, kind: 'calls' },
+ ]);
+ const diff = computeGraphDiff({ base, head });
+ expect(diff.addedEdges.length).toBeGreaterThan(0);
+ expect(diff.affectedNodeIds).toContain(a.id);
+ expect(diff.affectedNodeIds).toContain(b.id);
+ });
+});
diff --git a/packages/codedelta-graph-diff/package.json b/packages/codedelta-graph-diff/package.json
new file mode 100644
index 000000000..911e1f079
--- /dev/null
+++ b/packages/codedelta-graph-diff/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "@codedelta/graph-diff",
+ "version": "0.1.0",
+ "description": "Structural graph diff for CodeDelta (Phase 2)",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/types": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-graph-diff/src/index.ts b/packages/codedelta-graph-diff/src/index.ts
new file mode 100644
index 000000000..178ebc757
--- /dev/null
+++ b/packages/codedelta-graph-diff/src/index.ts
@@ -0,0 +1,165 @@
+import type { ChangedFile, CodeEdge, CodeGraphSnapshot, CodeNode, GraphDiff } from '@codedelta/types';
+
+export interface DiffOptions {
+ base: CodeGraphSnapshot;
+ head: CodeGraphSnapshot;
+ /** Git-level file changes between commits (optional). */
+ changedFiles?: ChangedFile[];
+}
+
+function nodeKey(node: CodeNode): string {
+ return node.id;
+}
+
+function edgeKey(edge: CodeEdge): string {
+ return `${edge.source}|${edge.target}|${edge.kind}`;
+}
+
+function detectNodeChanges(before: CodeNode, after: CodeNode): string[] {
+ const changes: string[] = [];
+ if (before.signature !== after.signature) changes.push('signature');
+ if (before.startLine !== after.startLine) changes.push('startLine');
+ if (before.endLine !== after.endLine) changes.push('endLine');
+ if (before.isExported !== after.isExported) changes.push('isExported');
+ if (before.name !== after.name) changes.push('name');
+ return changes;
+}
+
+function buildNodeMap(nodes: CodeNode[]): Map {
+ const map = new Map();
+ for (const n of nodes) {
+ map.set(nodeKey(n), n);
+ }
+ return map;
+}
+
+function buildEdgeMap(edges: CodeEdge[]): Map {
+ const map = new Map();
+ for (const e of edges) {
+ map.set(edgeKey(e), e);
+ }
+ return map;
+}
+
+const TRAVERSAL_KINDS = new Set(['calls', 'imports']);
+
+/**
+ * BFS from changed nodes over head snapshot edges (calls + imports).
+ */
+export function computeAffectedNodeIds(
+ head: CodeGraphSnapshot,
+ changedIds: Set,
+): string[] {
+ const adj = new Map();
+ for (const edge of head.edges) {
+ if (!TRAVERSAL_KINDS.has(edge.kind)) continue;
+ const out = adj.get(edge.source) ?? [];
+ out.push(edge.target);
+ adj.set(edge.source, out);
+ const inList = adj.get(edge.target) ?? [];
+ inList.push(edge.source);
+ adj.set(edge.target, inList);
+ }
+
+ const affected = new Set();
+ const queue = [...changedIds];
+ for (const id of queue) {
+ affected.add(id);
+ }
+
+ while (queue.length > 0) {
+ const current = queue.shift()!;
+ const neighbors = adj.get(current) ?? [];
+ for (const next of neighbors) {
+ if (!affected.has(next)) {
+ affected.add(next);
+ queue.push(next);
+ }
+ }
+ }
+
+ return Array.from(affected).sort();
+}
+
+/**
+ * Compare two graph snapshots (commit-to-commit structural delta).
+ */
+export function computeGraphDiff(options: DiffOptions): GraphDiff {
+ const { base, head, changedFiles = [] } = options;
+
+ const baseMap = buildNodeMap(base.nodes);
+ const headMap = buildNodeMap(head.nodes);
+
+ const addedNodes: CodeNode[] = [];
+ const removedNodes: CodeNode[] = [];
+ const modifiedNodes: GraphDiff['modifiedNodes'] = [];
+
+ for (const [key, node] of headMap) {
+ if (!baseMap.has(key)) {
+ addedNodes.push(node);
+ }
+ }
+
+ for (const [key, node] of baseMap) {
+ if (!headMap.has(key)) {
+ removedNodes.push(node);
+ }
+ }
+
+ for (const [key, after] of headMap) {
+ const before = baseMap.get(key);
+ if (!before) continue;
+ const changes = detectNodeChanges(before, after);
+ if (changes.length > 0) {
+ modifiedNodes.push({ before, after, changes });
+ }
+ }
+
+ const baseEdges = buildEdgeMap(base.edges);
+ const headEdges = buildEdgeMap(head.edges);
+
+ const addedEdges: CodeEdge[] = [];
+ const removedEdges: CodeEdge[] = [];
+
+ for (const [key, edge] of headEdges) {
+ if (!baseEdges.has(key)) addedEdges.push(edge);
+ }
+ for (const [key, edge] of baseEdges) {
+ if (!headEdges.has(key)) removedEdges.push(edge);
+ }
+
+ const changedIds = new Set([
+ ...addedNodes.map((n) => n.id),
+ ...removedNodes.map((n) => n.id),
+ ...modifiedNodes.map((m) => m.after.id),
+ ]);
+ for (const e of addedEdges) {
+ changedIds.add(e.source);
+ changedIds.add(e.target);
+ }
+ for (const e of removedEdges) {
+ changedIds.add(e.source);
+ changedIds.add(e.target);
+ }
+
+ const affectedNodeIds = computeAffectedNodeIds(head, changedIds);
+
+ return {
+ baseCommit: base.commitHash,
+ headCommit: head.commitHash,
+ addedNodes,
+ removedNodes,
+ modifiedNodes,
+ addedEdges,
+ removedEdges,
+ affectedNodeIds,
+ changedFiles,
+ summary: {
+ symbolsAdded: addedNodes.length,
+ symbolsRemoved: removedNodes.length,
+ symbolsModified: modifiedNodes.length,
+ edgesAdded: addedEdges.length,
+ edgesRemoved: removedEdges.length,
+ },
+ };
+}
diff --git a/packages/codedelta-graph-diff/tsconfig.json b/packages/codedelta-graph-diff/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-graph-diff/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-graph-subgraph/__tests__/graph-subgraph.test.ts b/packages/codedelta-graph-subgraph/__tests__/graph-subgraph.test.ts
new file mode 100644
index 000000000..b774b7e34
--- /dev/null
+++ b/packages/codedelta-graph-subgraph/__tests__/graph-subgraph.test.ts
@@ -0,0 +1,141 @@
+import { describe, expect, it } from 'vitest';
+import type { CodeGraphSnapshot } from '@codedelta/types';
+import {
+ buildCallTree,
+ buildDeltaPanoramaGraph,
+ buildPanoramaGraph,
+ detectEntryPoints,
+ findPath,
+} from '../src';
+
+function snap(
+ commitHash: string,
+ nodes: CodeGraphSnapshot['nodes'],
+ edges: CodeGraphSnapshot['edges'] = [],
+): CodeGraphSnapshot {
+ return {
+ repoId: 'test',
+ commitHash,
+ analyzerVersion: '0.1.0',
+ createdAt: new Date().toISOString(),
+ nodeCount: nodes.length,
+ edgeCount: edges.length,
+ nodes,
+ edges,
+ files: [...new Set(nodes.map((n) => n.filePath))],
+ };
+}
+
+function node(
+ id: string,
+ overrides: Partial = {},
+): CodeGraphSnapshot['nodes'][0] {
+ return {
+ id,
+ kind: 'function',
+ name: id.split('::').pop() ?? id,
+ qualifiedName: id,
+ filePath: id.split('::')[0] ?? 'src/a.ts',
+ language: 'typescript',
+ startLine: 1,
+ endLine: 10,
+ ...overrides,
+ };
+}
+
+describe('detectEntryPoints', () => {
+ it('prefers route and component nodes', () => {
+ const route = node('src/r.ts::GET /api', { kind: 'route', name: 'GET /api' });
+ const comp = node('src/App.tsx::App', { kind: 'component' });
+ const fn = node('src/util.ts::helper', { isExported: true });
+ const snapshot = snap('c1', [fn, comp, route]);
+ const entries = detectEntryPoints(snapshot);
+ expect(entries[0]).toBe(route.id);
+ expect(entries).toContain(comp.id);
+ });
+
+ it('includes exported functions with no callers', () => {
+ const a = node('src/a.ts::main', { isExported: true });
+ const b = node('src/a.ts::helper', { isExported: true });
+ const snapshot = snap(
+ 'c1',
+ [a, b],
+ [{ source: a.id, target: b.id, kind: 'calls' }],
+ );
+ const entries = detectEntryPoints(snapshot);
+ expect(entries).toContain(a.id);
+ expect(entries).not.toContain(b.id);
+ });
+});
+
+describe('buildCallTree', () => {
+ it('expands calls up to maxDepth', () => {
+ const a = node('src/a.ts::a');
+ const b = node('src/a.ts::b');
+ const c = node('src/a.ts::c');
+ const snapshot = snap('c1', [a, b, c], [
+ { source: a.id, target: b.id, kind: 'calls' },
+ { source: b.id, target: c.id, kind: 'calls' },
+ ]);
+ const tree = buildCallTree(snapshot, a.id, { maxDepth: 2, maxNodes: 50 });
+ expect(tree.nodeIds.has(c.id)).toBe(true);
+ expect(tree.edges).toHaveLength(2);
+ });
+});
+
+describe('findPath', () => {
+ it('finds shortest call path', () => {
+ const a = node('src/a.ts::a');
+ const b = node('src/a.ts::b');
+ const c = node('src/a.ts::c');
+ const snapshot = snap('c1', [a, b, c], [
+ { source: a.id, target: b.id, kind: 'calls' },
+ { source: b.id, target: c.id, kind: 'calls' },
+ ]);
+ expect(findPath(snapshot, a.id, c.id)).toEqual([a.id, b.id, c.id]);
+ });
+});
+
+describe('buildPanoramaGraph', () => {
+ it('returns positioned nodes', () => {
+ const route = node('src/r.ts::GET /', { kind: 'route', name: 'GET /' });
+ const handler = node('src/r.ts::handler');
+ const snapshot = snap('c1', [route, handler], [
+ { source: route.id, target: handler.id, kind: 'references' },
+ ]);
+ const graph = buildPanoramaGraph('repo1', snapshot, { rootId: route.id, maxDepth: 2 });
+ expect(graph.nodes.length).toBeGreaterThan(0);
+ expect(graph.nodes[0]?.position).toBeDefined();
+ });
+
+ it('expands sparse route entries and builds entry catalog on overview', () => {
+ const route = node('src/r.ts::USE /api', { kind: 'route', name: 'USE /api' });
+ const router = node('src/r.ts::apiRouter');
+ const handler = node('src/r.ts::listUsers');
+ const snapshot = snap('c1', [route, router, handler], [
+ { source: route.id, target: router.id, kind: 'references' },
+ { source: router.id, target: handler.id, kind: 'calls' },
+ ]);
+ const graph = buildPanoramaGraph('repo1', snapshot, { maxDepth: 2 });
+ expect(graph.entryCatalog?.length).toBeGreaterThan(0);
+ expect(graph.nodes.length).toBeGreaterThan(1);
+ expect(graph.stats.effectiveDepth).toBeDefined();
+ });
+});
+
+describe('buildDeltaPanoramaGraph', () => {
+ it('marks modified nodes in delta panorama', () => {
+ const route = node('src/r.ts::GET /', { kind: 'route', name: 'GET /' });
+ const handler = node('src/r.ts::handler', { endLine: 10 });
+ const handlerMod = node('src/r.ts::handler', { endLine: 20 });
+ const base = snap('base', [route, handler], [
+ { source: route.id, target: handler.id, kind: 'references' },
+ ]);
+ const head = snap('head', [route, handlerMod], [
+ { source: route.id, target: handlerMod.id, kind: 'references' },
+ ]);
+ const graph = buildDeltaPanoramaGraph('repo1', base, head, { rootId: route.id });
+ const h = graph.nodes.find((n) => n.id === handler.id);
+ expect(h?.deltaStatus).toBe('modified');
+ });
+});
diff --git a/packages/codedelta-graph-subgraph/package.json b/packages/codedelta-graph-subgraph/package.json
new file mode 100644
index 000000000..37981c0c5
--- /dev/null
+++ b/packages/codedelta-graph-subgraph/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@codedelta/graph-subgraph",
+ "version": "0.1.0",
+ "description": "Call-tree subgraph extraction and layout for CodeDelta panorama",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/graph-diff": "*",
+ "@codedelta/types": "*",
+ "@dagrejs/dagre": "^1.1.4"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-graph-subgraph/src/index.ts b/packages/codedelta-graph-subgraph/src/index.ts
new file mode 100644
index 000000000..47aa5d7ea
--- /dev/null
+++ b/packages/codedelta-graph-subgraph/src/index.ts
@@ -0,0 +1,858 @@
+import dagre from '@dagrejs/dagre';
+import { computeGraphDiff } from '@codedelta/graph-diff';
+import type {
+ CodeEdge,
+ CodeGraphSnapshot,
+ CodeNode,
+ GraphDiff,
+ PanoramaDeltaStatus,
+ PanoramaEdge,
+ PanoramaGraph,
+ PanoramaNode,
+ PanoramaNodeRole,
+} from '@codedelta/types';
+
+export interface DetectEntryPointsOptions {
+ limit?: number;
+}
+
+export interface BuildCallTreeOptions {
+ maxDepth?: number;
+ maxNodes?: number;
+ edgeKinds?: string[];
+}
+
+export interface BuildPanoramaOptions {
+ rootId?: string;
+ rootQuery?: string;
+ maxDepth?: number;
+ maxNodes?: number;
+ entryLimit?: number;
+ edgeKinds?: string[];
+ /** When set, only keep branches touching changed nodes/edges. */
+ deltaOnly?: boolean;
+}
+
+export interface TraceHighlightInput {
+ symbols?: string[];
+ entryPoints?: string[];
+ /** Resolve qualified names to node ids when possible. */
+ symbolQueries?: string[];
+}
+
+const DEFAULT_EDGE_KINDS = ['calls', 'references'];
+
+function nodeMap(snapshot: CodeGraphSnapshot): Map {
+ return new Map(snapshot.nodes.map((n) => [n.id, n]));
+}
+
+function edgeId(edge: Pick): string {
+ return `${edge.source}|${edge.target}|${edge.kind}`;
+}
+
+function buildOutgoing(
+ edges: CodeEdge[],
+ kinds: Set,
+): Map> {
+ const adj = new Map>();
+ for (const edge of edges) {
+ if (!kinds.has(edge.kind)) continue;
+ const list = adj.get(edge.source) ?? [];
+ list.push({ edge, target: edge.target });
+ adj.set(edge.source, list);
+ }
+ return adj;
+}
+
+function buildIncoming(
+ edges: CodeEdge[],
+ kinds: Set,
+): Map> {
+ const callers = new Map>();
+ for (const edge of edges) {
+ if (!kinds.has(edge.kind)) continue;
+ const set = callers.get(edge.target) ?? new Set();
+ set.add(edge.source);
+ callers.set(edge.target, set);
+ }
+ return callers;
+}
+
+function entryPriority(node: CodeNode): number {
+ if (node.kind === 'route') return 0;
+ if (node.kind === 'component') return 1;
+ if (node.isExported && (node.kind === 'function' || node.kind === 'method')) return 2;
+ return 99;
+}
+
+export function resolvePanoramaBudget(snapshotNodeCount: number): {
+ maxNodes: number;
+ entryLimit: number;
+ minPerRootNodes: number;
+ overviewDepthBonus: number;
+ sparseExpandLimit: number;
+} {
+ if (snapshotNodeCount >= 8000) {
+ return { maxNodes: 600, entryLimit: 20, minPerRootNodes: 56, overviewDepthBonus: 1, sparseExpandLimit: 18 };
+ }
+ if (snapshotNodeCount >= 3000) {
+ return { maxNodes: 480, entryLimit: 16, minPerRootNodes: 48, overviewDepthBonus: 1, sparseExpandLimit: 16 };
+ }
+ if (snapshotNodeCount >= 1000) {
+ return { maxNodes: 360, entryLimit: 12, minPerRootNodes: 40, overviewDepthBonus: 1, sparseExpandLimit: 14 };
+ }
+ if (snapshotNodeCount >= 300) {
+ return { maxNodes: 280, entryLimit: 10, minPerRootNodes: 32, overviewDepthBonus: 0, sparseExpandLimit: 12 };
+ }
+ return { maxNodes: 200, entryLimit: 6, minPerRootNodes: 28, overviewDepthBonus: 0, sparseExpandLimit: 10 };
+}
+
+/**
+ * Heuristic entry detection aligned with impact-score entry surface.
+ */
+export function detectEntryPoints(
+ snapshot: CodeGraphSnapshot,
+ options: DetectEntryPointsOptions = {},
+): string[] {
+ const limit = options.limit ?? 12;
+ const kinds = new Set(DEFAULT_EDGE_KINDS);
+ const callers = buildIncoming(snapshot.edges, kinds);
+ const candidates: CodeNode[] = [];
+
+ for (const node of snapshot.nodes) {
+ const p = entryPriority(node);
+ if (p === 99) continue;
+ if (p <= 1) {
+ candidates.push(node);
+ continue;
+ }
+ const inbound = callers.get(node.id);
+ if (!inbound || inbound.size === 0) {
+ candidates.push(node);
+ }
+ }
+
+ candidates.sort((a, b) => {
+ const pa = entryPriority(a);
+ const pb = entryPriority(b);
+ if (pa !== pb) return pa - pb;
+ return a.qualifiedName.localeCompare(b.qualifiedName);
+ });
+
+ const seen = new Set();
+ const ids: string[] = [];
+ for (const node of candidates) {
+ if (seen.has(node.id)) continue;
+ seen.add(node.id);
+ ids.push(node.id);
+ if (ids.length >= limit) break;
+ }
+ return ids;
+}
+
+export function resolveNodeQuery(snapshot: CodeGraphSnapshot, query: string): CodeNode | null {
+ const q = query.trim().toLowerCase();
+ if (!q) return null;
+
+ const exact = snapshot.nodes.filter(
+ (n) => n.id === query || n.qualifiedName === query || n.name === query,
+ );
+ if (exact.length === 1) return exact[0]!;
+ if (exact.length > 1) {
+ exact.sort((a, b) => entryPriority(a) - entryPriority(b));
+ return exact[0]!;
+ }
+
+ const partial = snapshot.nodes.filter(
+ (n) =>
+ n.qualifiedName.toLowerCase().includes(q) ||
+ n.name.toLowerCase() === q ||
+ n.filePath.toLowerCase().includes(q),
+ );
+ if (partial.length === 0) return null;
+ partial.sort((a, b) => entryPriority(a) - entryPriority(b));
+ return partial[0]!;
+}
+
+export interface CallTreeResult {
+ nodeIds: Set;
+ edges: CodeEdge[];
+ truncated: boolean;
+ roles: Map;
+}
+
+export function buildCallTree(
+ snapshot: CodeGraphSnapshot,
+ rootId: string,
+ options: BuildCallTreeOptions = {},
+): CallTreeResult {
+ const maxDepth = options.maxDepth ?? 3;
+ const maxNodes = options.maxNodes ?? 200;
+ const edgeKinds = new Set(options.edgeKinds ?? DEFAULT_EDGE_KINDS);
+ const nodes = nodeMap(snapshot);
+ if (!nodes.has(rootId)) {
+ return { nodeIds: new Set(), edges: [], truncated: false, roles: new Map() };
+ }
+
+ const outgoing = buildOutgoing(snapshot.edges, edgeKinds);
+ const nodeIds = new Set();
+ const edgeKeys = new Set();
+ const collected: CodeEdge[] = [];
+ const roles = new Map();
+ let truncated = false;
+
+ roles.set(rootId, 'entry');
+ const queue: Array<{ id: string; depth: number }> = [{ id: rootId, depth: 0 }];
+
+ while (queue.length > 0) {
+ const { id, depth } = queue.shift()!;
+ if (nodeIds.has(id)) continue;
+ if (nodeIds.size >= maxNodes) {
+ truncated = true;
+ break;
+ }
+ if (!nodes.has(id)) continue;
+
+ nodeIds.add(id);
+ if (depth >= maxDepth) continue;
+
+ for (const { edge, target } of outgoing.get(id) ?? []) {
+ const eid = edgeId(edge);
+ if (!edgeKeys.has(eid)) {
+ edgeKeys.add(eid);
+ collected.push(edge);
+ }
+ if (!nodeIds.has(target) && nodeIds.size + queue.length < maxNodes) {
+ if (!roles.has(target)) {
+ roles.set(target, depth + 1 >= maxDepth ? 'leaf' : 'bridge');
+ }
+ queue.push({ id: target, depth: depth + 1 });
+ } else if (nodeIds.size >= maxNodes) {
+ truncated = true;
+ }
+ }
+ }
+
+ for (const id of nodeIds) {
+ if (id === rootId) continue;
+ const hasOut = (outgoing.get(id) ?? []).some(({ target }) => nodeIds.has(target));
+ if (!hasOut) roles.set(id, 'leaf');
+ }
+
+ return { nodeIds, edges: collected, truncated, roles };
+}
+
+/** When an entry tree is mostly a lone route/mount, pull in reference targets + one call hop. */
+export function expandSparseEntryTree(
+ snapshot: CodeGraphSnapshot,
+ tree: CallTreeResult,
+ rootId: string,
+ options: { maxExtra?: number; edgeKinds?: string[] } = {},
+): CallTreeResult {
+ const sparseThreshold = 4;
+ if (tree.nodeIds.size >= sparseThreshold) return tree;
+
+ const maxExtra = options.maxExtra ?? 12;
+ const edgeKinds = new Set(options.edgeKinds ?? DEFAULT_EDGE_KINDS);
+ const outgoing = buildOutgoing(snapshot.edges, edgeKinds);
+ const extraTargets = new Set();
+
+ for (const { target } of outgoing.get(rootId) ?? []) {
+ if (!tree.nodeIds.has(target)) extraTargets.add(target);
+ }
+
+ for (const id of tree.nodeIds) {
+ if (id === rootId) continue;
+ for (const { target } of outgoing.get(id) ?? []) {
+ if (!tree.nodeIds.has(target)) extraTargets.add(target);
+ }
+ }
+
+ const toAdd = [...extraTargets].slice(0, maxExtra);
+ if (toAdd.length === 0) return tree;
+
+ const perTarget = Math.max(6, Math.floor(maxExtra / Math.max(1, toAdd.length)));
+ const extraTrees = toAdd.map((id) =>
+ buildCallTree(snapshot, id, { maxDepth: 2, maxNodes: perTarget, edgeKinds: options.edgeKinds }),
+ );
+ return mergeTrees([tree, ...extraTrees]);
+}
+
+function buildEntryCatalog(
+ snapshot: CodeGraphSnapshot,
+ entryIds: string[],
+ graphNodeIds: Set,
+): Array<{ id: string; qualifiedName: string; kind: string; inGraph: boolean }> {
+ const nodes = nodeMap(snapshot);
+ return entryIds.map((id) => {
+ const node = nodes.get(id);
+ return {
+ id,
+ qualifiedName: node?.qualifiedName ?? id,
+ kind: node?.kind ?? 'function',
+ inGraph: graphNodeIds.has(id),
+ };
+ });
+}
+
+export function findPath(
+ snapshot: CodeGraphSnapshot,
+ fromId: string,
+ toId: string,
+ edgeKinds: string[] = ['calls'],
+): string[] | null {
+ if (fromId === toId) return [fromId];
+ const kinds = new Set(edgeKinds);
+ const outgoing = buildOutgoing(snapshot.edges, kinds);
+ const prev = new Map();
+ const queue = [fromId];
+ prev.set(fromId, null);
+
+ while (queue.length > 0) {
+ const current = queue.shift()!;
+ if (current === toId) {
+ const path: string[] = [];
+ let cur: string | null = toId;
+ while (cur) {
+ path.unshift(cur);
+ cur = prev.get(cur) ?? null;
+ }
+ return path;
+ }
+ for (const { target } of outgoing.get(current) ?? []) {
+ if (prev.has(target)) continue;
+ prev.set(target, current);
+ queue.push(target);
+ }
+ }
+ return null;
+}
+
+function toPanoramaNode(node: CodeNode, role?: PanoramaNodeRole, commitHash?: string): PanoramaNode {
+ const short = commitHash ? commitHash.slice(0, 7) : undefined;
+ return {
+ id: node.id,
+ kind: node.kind,
+ name: node.name,
+ qualifiedName: node.qualifiedName,
+ filePath: node.filePath,
+ startLine: node.startLine,
+ endLine: node.endLine,
+ signature: node.signature,
+ commitHash,
+ commitShortHash: short,
+ role,
+ };
+}
+
+function toPanoramaEdge(edge: CodeEdge): PanoramaEdge {
+ const synthesizedBy =
+ edge.provenance === 'heuristic' && edge.metadata && typeof edge.metadata.synthesizedBy === 'string'
+ ? edge.metadata.synthesizedBy
+ : undefined;
+ return {
+ id: edgeId(edge),
+ source: edge.source,
+ target: edge.target,
+ kind: edge.kind,
+ line: edge.line,
+ provenance: edge.provenance,
+ synthesizedBy,
+ };
+}
+
+function mergeTrees(trees: CallTreeResult[]): CallTreeResult {
+ const nodeIds = new Set();
+ const edgeKeys = new Set();
+ const edges: CodeEdge[] = [];
+ const roles = new Map();
+ let truncated = false;
+
+ for (const tree of trees) {
+ if (tree.truncated) truncated = true;
+ for (const id of tree.nodeIds) nodeIds.add(id);
+ for (const [id, role] of tree.roles) {
+ const existing = roles.get(id);
+ if (!existing || role === 'entry') roles.set(id, role);
+ }
+ for (const edge of tree.edges) {
+ const eid = edgeId(edge);
+ if (!edgeKeys.has(eid)) {
+ edgeKeys.add(eid);
+ edges.push(edge);
+ }
+ }
+ }
+ return { nodeIds, edges, truncated, roles };
+}
+
+function filterDeltaRelevant(
+ graph: { nodes: PanoramaNode[]; edges: PanoramaEdge[] },
+): { nodes: PanoramaNode[]; edges: PanoramaEdge[] } {
+ const changedNodeIds = new Set(
+ graph.nodes.filter((n) => n.deltaStatus && n.deltaStatus !== 'unchanged').map((n) => n.id),
+ );
+ const changedEdgeIds = new Set(
+ graph.edges.filter((e) => e.deltaStatus && e.deltaStatus !== 'unchanged').map((e) => e.id),
+ );
+ if (changedNodeIds.size === 0 && changedEdgeIds.size === 0) {
+ return graph;
+ }
+
+ const adj = new Map();
+ for (const e of graph.edges) {
+ const out = adj.get(e.source) ?? [];
+ out.push(e.target);
+ adj.set(e.source, out);
+ const inList = adj.get(e.target) ?? [];
+ inList.push(e.source);
+ adj.set(e.target, inList);
+ }
+
+ const keep = new Set(changedNodeIds);
+ const queue = [...changedNodeIds];
+ while (queue.length > 0) {
+ const id = queue.shift()!;
+ for (const next of adj.get(id) ?? []) {
+ if (!keep.has(next)) {
+ keep.add(next);
+ queue.push(next);
+ }
+ }
+ }
+
+ return {
+ nodes: graph.nodes.filter((n) => keep.has(n.id)),
+ edges: graph.edges.filter((e) => keep.has(e.source) && keep.has(e.target)),
+ };
+}
+
+export function applyDeltaOverlay(
+ nodes: PanoramaNode[],
+ edges: PanoramaEdge[],
+ diff: GraphDiff,
+ headCommitHash?: string,
+): { nodes: PanoramaNode[]; edges: PanoramaEdge[] } {
+ const addedIds = new Set(diff.addedNodes.map((n) => n.id));
+ const removedIds = new Set(diff.removedNodes.map((n) => n.id));
+ const modifiedIds = new Set(diff.modifiedNodes.map((m) => m.after.id));
+ const addedEdgeIds = new Set(diff.addedEdges.map((e) => edgeId(e)));
+ const removedEdgeIds = new Set(diff.removedEdges.map((e) => edgeId(e)));
+
+ const nodeById = new Map(nodes.map((n) => [n.id, n]));
+ for (const removed of diff.removedNodes) {
+ if (!nodeById.has(removed.id)) {
+ nodes.push({
+ ...toPanoramaNode(removed, 'leaf', headCommitHash),
+ deltaStatus: 'removed',
+ });
+ }
+ }
+
+ const nextNodes = nodes.map((n) => {
+ let deltaStatus: PanoramaDeltaStatus = 'unchanged';
+ if (addedIds.has(n.id)) deltaStatus = 'added';
+ else if (removedIds.has(n.id)) deltaStatus = 'removed';
+ else if (modifiedIds.has(n.id)) deltaStatus = 'modified';
+ return { ...n, deltaStatus };
+ });
+
+ const nextEdges = edges.map((e) => {
+ let deltaStatus: 'added' | 'removed' | 'unchanged' = 'unchanged';
+ if (addedEdgeIds.has(e.id)) deltaStatus = 'added';
+ else if (removedEdgeIds.has(e.id)) deltaStatus = 'removed';
+ return { ...e, deltaStatus };
+ });
+
+ for (const removed of diff.removedEdges) {
+ const id = edgeId(removed);
+ if (!nextEdges.some((e) => e.id === id)) {
+ nextEdges.push({ ...toPanoramaEdge(removed), deltaStatus: 'removed' });
+ }
+ }
+
+ return { nodes: nextNodes, edges: nextEdges };
+}
+
+export function applyTraceHighlight(
+ snapshot: CodeGraphSnapshot,
+ nodes: PanoramaNode[],
+ edges: PanoramaEdge[],
+ input: TraceHighlightInput,
+): { nodes: PanoramaNode[]; edges: PanoramaEdge[]; pathNodeIds: string[]; pathConnected: boolean; pathMessage?: string } {
+ const highlightIds = new Set();
+ const queries = [
+ ...(input.symbols ?? []),
+ ...(input.entryPoints ?? []),
+ ...(input.symbolQueries ?? []),
+ ];
+
+ for (const q of queries) {
+ const node = resolveNodeQuery(snapshot, q);
+ if (node) highlightIds.add(node.id);
+ for (const n of snapshot.nodes) {
+ if (n.qualifiedName === q || n.name === q) highlightIds.add(n.id);
+ }
+ }
+
+ const entryIds = nodes.filter((n) => n.role === 'entry').map((n) => n.id);
+ const targetIds = [...highlightIds].filter((id) => nodes.some((n) => n.id === id));
+
+ let pathNodeIds: string[] = [];
+ let pathConnected = false;
+ let pathMessage: string | undefined;
+
+ if (entryIds.length > 0 && targetIds.length > 0) {
+ outer: for (const entry of entryIds) {
+ for (const target of targetIds) {
+ if (entry === target) {
+ pathNodeIds = [entry];
+ pathConnected = true;
+ break outer;
+ }
+ const path = findPath(snapshot, entry, target);
+ if (path && path.length > 0) {
+ pathNodeIds = path;
+ pathConnected = true;
+ break outer;
+ }
+ }
+ }
+ if (!pathConnected) {
+ pathMessage = 'Trace evidence symbols are in the graph but no call path connects them to the shown entry points.';
+ }
+ }
+
+ const pathSet = new Set(pathNodeIds);
+ const nextNodes = nodes.map((n) => ({
+ ...n,
+ traceHighlight: highlightIds.has(n.id),
+ pathHighlight: pathSet.has(n.id),
+ }));
+
+ const pathEdgeIds = new Set();
+ for (let i = 0; i < pathNodeIds.length - 1; i++) {
+ const from = pathNodeIds[i]!;
+ const to = pathNodeIds[i + 1]!;
+ for (const e of edges) {
+ if (e.source === from && e.target === to) pathEdgeIds.add(e.id);
+ }
+ }
+
+ const nextEdges = edges.map((e) => ({
+ ...e,
+ pathHighlight: pathEdgeIds.has(e.id),
+ }));
+
+ return { nodes: nextNodes, edges: nextEdges, pathNodeIds, pathConnected, pathMessage };
+}
+
+function collectForest(
+ entryId: string,
+ allNodes: PanoramaNode[],
+ edges: PanoramaEdge[],
+): { nodes: PanoramaNode[]; edges: PanoramaEdge[] } {
+ const nodeIds = new Set();
+ const queue = [entryId];
+ while (queue.length > 0) {
+ const id = queue.shift()!;
+ if (nodeIds.has(id)) continue;
+ if (!allNodes.some((n) => n.id === id)) continue;
+ nodeIds.add(id);
+ for (const edge of edges) {
+ if (edge.source === id && allNodes.some((n) => n.id === edge.target)) {
+ queue.push(edge.target);
+ }
+ }
+ }
+ return {
+ nodes: allNodes.filter((n) => nodeIds.has(n.id)),
+ edges: edges.filter((e) => nodeIds.has(e.source) && nodeIds.has(e.target)),
+ };
+}
+
+function layoutForest(nodes: PanoramaNode[], edges: PanoramaEdge[]): PanoramaNode[] {
+ if (nodes.length === 0) return nodes;
+
+ const g = new dagre.graphlib.Graph();
+ g.setDefaultEdgeLabel(() => ({}));
+ g.setGraph({ rankdir: 'TB', nodesep: 90, ranksep: 140, marginx: 60, marginy: 60 });
+
+ const width = 360;
+ const height = 188;
+
+ for (const node of nodes) {
+ g.setNode(node.id, { width, height });
+ }
+ for (const edge of edges) {
+ if (nodes.some((n) => n.id === edge.source) && nodes.some((n) => n.id === edge.target)) {
+ g.setEdge(edge.source, edge.target);
+ }
+ }
+
+ dagre.layout(g);
+
+ return nodes.map((node) => {
+ const laid = g.node(node.id);
+ if (!laid) return node;
+ return {
+ ...node,
+ position: {
+ x: laid.x - width / 2,
+ y: laid.y - height / 2,
+ },
+ };
+ });
+}
+
+export function layoutPanorama(
+ nodes: PanoramaNode[],
+ edges: PanoramaEdge[],
+ entryPointIds?: string[],
+): PanoramaNode[] {
+ if (nodes.length === 0) return nodes;
+
+ const nodeWidth = 360;
+ const entries =
+ entryPointIds?.filter((id) => nodes.some((n) => n.id === id)) ??
+ nodes.filter((n) => n.role === 'entry').map((n) => n.id);
+
+ if (entries.length <= 1) {
+ return layoutForest(nodes, edges);
+ }
+
+ const positioned = new Map();
+ let xOffset = 0;
+ const forestGap = 120;
+
+ for (const entryId of entries) {
+ const forest = collectForest(entryId, nodes, edges);
+ if (forest.nodes.length === 0) continue;
+
+ const laid = layoutForest(forest.nodes, forest.edges);
+ let minX = Infinity;
+ let maxX = -Infinity;
+ for (const n of laid) {
+ const x = n.position?.x ?? 0;
+ minX = Math.min(minX, x);
+ maxX = Math.max(maxX, x + nodeWidth);
+ }
+ const shift = Number.isFinite(minX) ? xOffset - minX : xOffset;
+
+ for (const n of laid) {
+ positioned.set(n.id, {
+ ...n,
+ position: {
+ x: (n.position?.x ?? 0) + shift,
+ y: n.position?.y ?? 0,
+ },
+ });
+ }
+
+ xOffset += (Number.isFinite(maxX) && Number.isFinite(minX) ? maxX - minX : nodeWidth) + forestGap;
+ }
+
+ return nodes.map((n) => positioned.get(n.id) ?? n);
+}
+
+export function buildPanoramaGraph(
+ repoId: string,
+ snapshot: CodeGraphSnapshot,
+ options: BuildPanoramaOptions = {},
+): PanoramaGraph {
+ const maxDepth = options.maxDepth ?? 3;
+ const budget = resolvePanoramaBudget(snapshot.nodes.length);
+ const maxNodes = options.maxNodes ?? budget.maxNodes;
+ const entryLimit = options.entryLimit ?? budget.entryLimit;
+ const edgeKinds = options.edgeKinds ?? DEFAULT_EDGE_KINDS;
+ const nodesById = nodeMap(snapshot);
+ const isOverview = !options.rootId && !options.rootQuery;
+ const effectiveDepth = isOverview ? maxDepth + budget.overviewDepthBonus : maxDepth;
+
+ let tree: CallTreeResult;
+ let entryPoints: string[] = [];
+ let catalogEntryIds: string[] = [];
+
+ if (options.rootId) {
+ entryPoints = [options.rootId];
+ tree = buildCallTree(snapshot, options.rootId, { maxDepth: effectiveDepth, maxNodes, edgeKinds });
+ } else if (options.rootQuery) {
+ const resolved = resolveNodeQuery(snapshot, options.rootQuery);
+ if (!resolved) {
+ return {
+ repoId,
+ commit: snapshot.commitHash,
+ nodes: [],
+ edges: [],
+ entryPoints: [],
+ layout: 'layered',
+ stats: { nodeCount: 0, edgeCount: 0, truncated: false },
+ };
+ }
+ entryPoints = [resolved.id];
+ tree = buildCallTree(snapshot, resolved.id, { maxDepth: effectiveDepth, maxNodes, edgeKinds });
+ } else {
+ catalogEntryIds = detectEntryPoints(snapshot, { limit: entryLimit });
+ entryPoints = catalogEntryIds;
+ const perRootBudget = Math.max(
+ budget.minPerRootNodes,
+ Math.floor(maxNodes / Math.max(1, entryPoints.length)),
+ );
+ const trees = entryPoints.map((rootId) => {
+ let sub = buildCallTree(snapshot, rootId, {
+ maxDepth: effectiveDepth,
+ maxNodes: perRootBudget,
+ edgeKinds,
+ });
+ sub = expandSparseEntryTree(snapshot, sub, rootId, {
+ maxExtra: budget.sparseExpandLimit,
+ edgeKinds,
+ });
+ return sub;
+ });
+ tree = mergeTrees(trees);
+ }
+
+ let panoramaNodes: PanoramaNode[] = [...tree.nodeIds]
+ .map((id) => {
+ const node = nodesById.get(id);
+ if (!node) return null;
+ return toPanoramaNode(node, tree.roles.get(id), snapshot.commitHash);
+ })
+ .filter((n): n is PanoramaNode => n !== null);
+
+ let panoramaEdges: PanoramaEdge[] = tree.edges.map(toPanoramaEdge);
+
+ if (options.deltaOnly) {
+ const filtered = filterDeltaRelevant({ nodes: panoramaNodes, edges: panoramaEdges });
+ panoramaNodes = filtered.nodes;
+ panoramaEdges = filtered.edges;
+ }
+
+ panoramaNodes = layoutPanorama(panoramaNodes, panoramaEdges, entryPoints);
+
+ const entryCatalog =
+ catalogEntryIds.length > 0
+ ? buildEntryCatalog(snapshot, catalogEntryIds, tree.nodeIds)
+ : undefined;
+
+ return {
+ repoId,
+ commit: snapshot.commitHash,
+ commitShortHash: snapshot.commitHash.slice(0, 7),
+ nodes: panoramaNodes,
+ edges: panoramaEdges,
+ entryPoints,
+ entryCatalog,
+ layout: 'layered',
+ stats: {
+ nodeCount: panoramaNodes.length,
+ edgeCount: panoramaEdges.length,
+ truncated: tree.truncated,
+ snapshotNodeCount: snapshot.nodes.length,
+ entrySurfaceCount: entryPoints.length,
+ effectiveDepth,
+ },
+ extractionMethod: snapshot.metadata?.extractionMethod,
+ };
+}
+
+export function buildDeltaPanoramaGraph(
+ repoId: string,
+ base: CodeGraphSnapshot,
+ head: CodeGraphSnapshot,
+ options: BuildPanoramaOptions & {
+ seedEntryPoints?: string[];
+ } = {},
+): PanoramaGraph {
+ const diff = computeGraphDiff({ base, head });
+ const seedIds: string[] = [];
+
+ if (options.rootId) {
+ seedIds.push(options.rootId);
+ } else if (options.rootQuery) {
+ const resolved = resolveNodeQuery(head, options.rootQuery);
+ if (resolved) seedIds.push(resolved.id);
+ } else if (options.seedEntryPoints?.length) {
+ for (const q of options.seedEntryPoints) {
+ const resolved = resolveNodeQuery(head, q);
+ if (resolved) seedIds.push(resolved.id);
+ }
+ }
+
+ if (seedIds.length === 0) {
+ const impacted = new Set();
+ for (const n of diff.addedNodes) impacted.add(n.id);
+ for (const n of diff.modifiedNodes) impacted.add(n.after.id);
+ for (const id of diff.affectedNodeIds.slice(0, 20)) impacted.add(id);
+ for (const node of head.nodes) {
+ if (impacted.has(node.id) && entryPriority(node) <= 2) {
+ seedIds.push(node.id);
+ }
+ }
+ }
+
+ if (seedIds.length === 0) {
+ const budget = resolvePanoramaBudget(head.nodes.length);
+ seedIds.push(...detectEntryPoints(head, { limit: options.entryLimit ?? budget.entryLimit }));
+ }
+
+ const budget = resolvePanoramaBudget(head.nodes.length);
+ const uniqueSeeds = [...new Set(seedIds)].slice(0, options.entryLimit ?? budget.entryLimit);
+ const perRootBudget = Math.max(
+ 40,
+ Math.floor((options.maxNodes ?? budget.maxNodes) / Math.max(1, uniqueSeeds.length)),
+ );
+ const trees = uniqueSeeds.map((rootId) =>
+ buildCallTree(head, rootId, {
+ maxDepth: options.maxDepth ?? 3,
+ maxNodes: perRootBudget,
+ edgeKinds: options.edgeKinds ?? DEFAULT_EDGE_KINDS,
+ }),
+ );
+ const tree = mergeTrees(trees);
+ const nodesById = nodeMap(head);
+
+ let panoramaNodes: PanoramaNode[] = [...tree.nodeIds]
+ .map((id) => {
+ const node = nodesById.get(id);
+ if (!node) return null;
+ return toPanoramaNode(node, tree.roles.get(id), head.commitHash);
+ })
+ .filter((n): n is PanoramaNode => n !== null);
+
+ let panoramaEdges: PanoramaEdge[] = tree.edges.map(toPanoramaEdge);
+ const overlaid = applyDeltaOverlay(panoramaNodes, panoramaEdges, diff, head.commitHash);
+ panoramaNodes = overlaid.nodes;
+ panoramaEdges = overlaid.edges;
+
+ if (options.deltaOnly !== false) {
+ const filtered = filterDeltaRelevant({ nodes: panoramaNodes, edges: panoramaEdges });
+ panoramaNodes = filtered.nodes;
+ panoramaEdges = filtered.edges;
+ }
+
+ panoramaNodes = layoutPanorama(panoramaNodes, panoramaEdges, uniqueSeeds);
+
+ return {
+ repoId,
+ base: base.commitHash,
+ head: head.commitHash,
+ commit: head.commitHash,
+ commitShortHash: head.commitHash.slice(0, 7),
+ nodes: panoramaNodes,
+ edges: panoramaEdges,
+ entryPoints: uniqueSeeds,
+ layout: 'layered',
+ stats: {
+ nodeCount: panoramaNodes.length,
+ edgeCount: panoramaEdges.length,
+ truncated: tree.truncated,
+ snapshotNodeCount: head.nodes.length,
+ entrySurfaceCount: uniqueSeeds.length,
+ },
+ extractionMethod: head.metadata?.extractionMethod,
+ };
+}
+
+export { computeGraphDiff };
diff --git a/packages/codedelta-graph-subgraph/tsconfig.json b/packages/codedelta-graph-subgraph/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-graph-subgraph/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-impact-score/__tests__/impact-score.test.ts b/packages/codedelta-impact-score/__tests__/impact-score.test.ts
new file mode 100644
index 000000000..8d83030c2
--- /dev/null
+++ b/packages/codedelta-impact-score/__tests__/impact-score.test.ts
@@ -0,0 +1,211 @@
+import { describe, expect, it } from 'vitest';
+import type { CodeGraphSnapshot, GraphDiff } from '@codedelta/types';
+import { __private, computeImpactScore } from '../src';
+
+function minimalDiff(overrides: Partial = {}): GraphDiff {
+ return {
+ baseCommit: 'base',
+ headCommit: 'head',
+ addedNodes: [],
+ removedNodes: [],
+ modifiedNodes: [],
+ addedEdges: [],
+ removedEdges: [],
+ affectedNodeIds: [],
+ changedFiles: [],
+ summary: {
+ symbolsAdded: 0,
+ symbolsRemoved: 0,
+ symbolsModified: 0,
+ edgesAdded: 0,
+ edgesRemoved: 0,
+ },
+ ...overrides,
+ };
+}
+
+function makeHeadSnapshot(totalNodes = 300): CodeGraphSnapshot {
+ return {
+ repoId: 'r',
+ commitHash: 'head',
+ analyzerVersion: 'test',
+ createdAt: new Date().toISOString(),
+ nodeCount: totalNodes,
+ edgeCount: 0,
+ files: [],
+ nodes: Array.from({ length: totalNodes }, (_, i) => ({
+ id: `n${i}`,
+ kind: i < 40 ? 'function' : i < 70 ? 'component' : 'method',
+ name: `node${i}`,
+ qualifiedName: `q.node${i}`,
+ filePath: i < 100 ? `src/mod${i % 10}/file.ts` : `src/core/file${i}.ts`,
+ language: 'typescript',
+ startLine: 1,
+ endLine: 2,
+ isExported: i < 40,
+ })),
+ edges: [],
+ };
+}
+
+describe('computeImpactScore', () => {
+ it('returns higher score for more changes', () => {
+ const head = makeHeadSnapshot();
+ const low = computeImpactScore('head', minimalDiff());
+ const high = computeImpactScore(
+ 'head',
+ minimalDiff({
+ changedFiles: [{ path: 'src/auth/login.ts', status: 'modified' }],
+ addedNodes: [
+ {
+ id: 'src/auth/login.ts::handleLogin',
+ kind: 'function',
+ name: 'handleLogin',
+ qualifiedName: 'src/auth/login.ts::handleLogin',
+ filePath: 'src/auth/login.ts',
+ language: 'typescript',
+ startLine: 1,
+ endLine: 20,
+ },
+ ],
+ summary: {
+ symbolsAdded: 1,
+ symbolsRemoved: 0,
+ symbolsModified: 0,
+ edgesAdded: 2,
+ edgesRemoved: 0,
+ },
+ affectedNodeIds: ['src/auth/login.ts::handleLogin'],
+ }),
+ head,
+ );
+ expect(high.score).toBeGreaterThan(low.score);
+ });
+
+ it('detects auth risk tag from paths', () => {
+ const diff = minimalDiff({
+ changedFiles: [{ path: 'src/auth/session.ts', status: 'modified' }],
+ addedNodes: [
+ {
+ id: 'src/auth/session.ts::refresh',
+ kind: 'function',
+ name: 'refresh',
+ qualifiedName: 'src/auth/session.ts::refresh',
+ filePath: 'src/auth/session.ts',
+ language: 'typescript',
+ startLine: 1,
+ endLine: 5,
+ },
+ ],
+ summary: { symbolsAdded: 1, symbolsRemoved: 0, symbolsModified: 0, edgesAdded: 0, edgesRemoved: 0 },
+ });
+ const impact = computeImpactScore('head', diff);
+ expect(impact.riskTags).toContain('auth');
+ });
+
+ it('clamps score to 0-100', () => {
+ const diff = minimalDiff({
+ changedFiles: Array.from({ length: 50 }, (_, i) => ({
+ path: `src/f${i}.ts`,
+ status: 'modified' as const,
+ })),
+ summary: {
+ symbolsAdded: 100,
+ symbolsRemoved: 100,
+ symbolsModified: 50,
+ edgesAdded: 200,
+ edgesRemoved: 200,
+ },
+ affectedNodeIds: Array.from({ length: 500 }, (_, i) => `n${i}`),
+ });
+ const impact = computeImpactScore('head', diff);
+ expect(impact.score).toBeLessThanOrEqual(100);
+ expect(impact.score).toBeGreaterThanOrEqual(0);
+ });
+
+ it('provides explanation and severity label', () => {
+ const head = makeHeadSnapshot();
+ const impact = computeImpactScore(
+ 'head',
+ minimalDiff({
+ changedFiles: [{ path: 'src/api/routes.ts', status: 'modified' }],
+ summary: {
+ symbolsAdded: 30,
+ symbolsRemoved: 10,
+ symbolsModified: 20,
+ edgesAdded: 40,
+ edgesRemoved: 20,
+ },
+ affectedNodeIds: Array.from({ length: 50 }, (_, i) => `n${i}`),
+ }),
+ head,
+ );
+ expect(impact.explanation).toBeDefined();
+ expect(impact.explanation?.reasons.length).toBeGreaterThan(0);
+ expect(['low', 'medium', 'high', 'critical']).toContain(impact.explanation?.severity);
+ });
+
+ it('does not saturate to 100 for medium-large diffs', () => {
+ const head = makeHeadSnapshot();
+ const impact = computeImpactScore(
+ 'head',
+ minimalDiff({
+ changedFiles: [
+ { path: 'README.md', status: 'modified' },
+ { path: 'src-tauri/src/store/mod.rs', status: 'modified' },
+ ],
+ summary: {
+ symbolsAdded: 80,
+ symbolsRemoved: 20,
+ symbolsModified: 50,
+ edgesAdded: 180,
+ edgesRemoved: 85,
+ },
+ affectedNodeIds: Array.from({ length: 157 }, (_, i) => `n${i}`),
+ }),
+ head,
+ );
+ expect(impact.score).toBeLessThan(100);
+ expect(impact.score).toBeGreaterThanOrEqual(35);
+ });
+
+ it('prefers wider blast radius over same-size edit', () => {
+ const head = makeHeadSnapshot(400);
+ const sameChangeShape = {
+ changedFiles: [{ path: 'src/core/a.ts', status: 'modified' as const }],
+ summary: {
+ symbolsAdded: 10,
+ symbolsRemoved: 5,
+ symbolsModified: 5,
+ edgesAdded: 15,
+ edgesRemoved: 10,
+ },
+ };
+
+ const narrow = computeImpactScore(
+ 'head',
+ minimalDiff({
+ ...sameChangeShape,
+ affectedNodeIds: Array.from({ length: 10 }, (_, i) => `n${i}`),
+ }),
+ head,
+ );
+ const wide = computeImpactScore(
+ 'head',
+ minimalDiff({
+ ...sameChangeShape,
+ affectedNodeIds: Array.from({ length: 180 }, (_, i) => `n${i}`),
+ }),
+ head,
+ );
+
+ expect(wide.score).toBeGreaterThan(narrow.score);
+ });
+
+ it('maps severity thresholds correctly', () => {
+ expect(__private.severityFromScore(20)).toBe('low');
+ expect(__private.severityFromScore(40)).toBe('medium');
+ expect(__private.severityFromScore(70)).toBe('high');
+ expect(__private.severityFromScore(90)).toBe('critical');
+ });
+});
diff --git a/packages/codedelta-impact-score/package.json b/packages/codedelta-impact-score/package.json
new file mode 100644
index 000000000..c924f79b1
--- /dev/null
+++ b/packages/codedelta-impact-score/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@codedelta/impact-score",
+ "version": "0.1.0",
+ "description": "Commit impact scoring for CodeDelta (Phase 2)",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/types": "*",
+ "@codedelta/graph-diff": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-impact-score/src/index.ts b/packages/codedelta-impact-score/src/index.ts
new file mode 100644
index 000000000..d19f053dc
--- /dev/null
+++ b/packages/codedelta-impact-score/src/index.ts
@@ -0,0 +1,271 @@
+import type {
+ CodeGraphSnapshot,
+ GraphDiff,
+ ImpactExplanation,
+ ImpactSeverity,
+ ImpactSummary,
+} from '@codedelta/types';
+
+/** Impact-first deterministic scoring weights. */
+const S_AFFECTED_RADIUS = 45;
+const S_ENTRY_SURFACE = 20;
+const S_MODULE_SPREAD = 15;
+const S_STRUCTURAL_CHURN = 12;
+const S_RISK_BONUS = 8;
+
+const RISK_RULES: Array<{ tag: string; patterns: RegExp[]; reason: string }> = [
+ { tag: 'auth', patterns: [/auth/i, /login/i, /session/i, /jwt/i, /oauth/i], reason: 'touches authentication/session logic' },
+ { tag: 'billing', patterns: [/billing/i, /stripe/i, /payment/i, /invoice/i], reason: 'touches billing or payment flow' },
+ { tag: 'database', patterns: [/prisma/i, /schema/i, /\bsql\b/i, /orm/i, /database/i], reason: 'touches data/storage boundaries' },
+ { tag: 'migration', patterns: [/migration/i], reason: 'includes migration-like changes' },
+ { tag: 'env', patterns: [/\.env/i, /config\/env/i], reason: 'touches runtime environment configuration' },
+ { tag: 'config', patterns: [/config/i, /settings/i], reason: 'touches global configuration' },
+ { tag: 'api', patterns: [/api\//i, /routes?\//i, /handler/i, /controller/i], reason: 'touches API surface' },
+ { tag: 'routing', patterns: [/route/i, /router/i, /pages\//i], reason: 'touches routing/entry behavior' },
+ { tag: 'dependency', patterns: [/package\.json/i, /Cargo\.toml/i, /Cargo\.lock/i, /import/i], reason: 'touches dependencies/import graph' },
+];
+
+function clamp(n: number, min: number, max: number): number {
+ return Math.max(min, Math.min(max, n));
+}
+
+function collectRiskTags(diff: GraphDiff): string[] {
+ const tags = new Set();
+ const texts: string[] = [];
+
+ for (const f of diff.changedFiles) {
+ texts.push(f.path);
+ }
+ for (const n of [...diff.addedNodes, ...diff.removedNodes, ...diff.modifiedNodes.map((m) => m.after)]) {
+ texts.push(n.filePath, n.name, n.qualifiedName);
+ }
+
+ const haystack = texts.join('\n');
+ for (const rule of RISK_RULES) {
+ if (rule.patterns.some((p) => p.test(haystack))) {
+ tags.add(rule.tag);
+ }
+ }
+
+ return Array.from(tags).sort();
+}
+
+function topLevelModule(filePath: string): string {
+ const parts = filePath.split('/').filter(Boolean);
+ if (parts.length >= 2) return `${parts[0]}/${parts[1]}`;
+ return parts[0] ?? filePath;
+}
+
+function impactedModules(diff: GraphDiff): string[] {
+ const modules = new Set();
+ for (const f of diff.changedFiles) {
+ modules.add(topLevelModule(f.path));
+ }
+ for (const n of [...diff.addedNodes, ...diff.removedNodes, ...diff.modifiedNodes.map((m) => m.after)]) {
+ modules.add(topLevelModule(n.filePath));
+ }
+ return Array.from(modules).sort();
+}
+
+function impactedModulesFromAffected(
+ diff: GraphDiff,
+ head: CodeGraphSnapshot | undefined,
+ affectedIds: Set,
+): string[] {
+ if (!head) return impactedModules(diff);
+ const modules = new Set();
+ for (const node of head.nodes) {
+ if (!affectedIds.has(node.id)) continue;
+ modules.add(topLevelModule(node.filePath));
+ }
+ if (modules.size === 0) return impactedModules(diff);
+ return Array.from(modules).sort();
+}
+
+function impactedEntryPoints(head: CodeGraphSnapshot, affectedIds: Set): string[] {
+ const entries: string[] = [];
+ for (const node of head.nodes) {
+ if (!affectedIds.has(node.id)) continue;
+ if (node.kind === 'route' || node.kind === 'component') {
+ entries.push(node.qualifiedName);
+ } else if (node.isExported && (node.kind === 'function' || node.kind === 'method')) {
+ entries.push(node.qualifiedName);
+ }
+ }
+ return [...new Set(entries)].sort();
+}
+
+function severityFromScore(score: number): ImpactSeverity {
+ if (score >= 90) return 'critical';
+ if (score >= 65) return 'high';
+ if (score >= 35) return 'medium';
+ return 'low';
+}
+
+function normalize(count: number, weight: number, cap: number): number {
+ if (count <= 0) return 0;
+ return Math.min(cap, Math.log1p(count) * weight);
+}
+
+function ratio(value: number, total: number): number {
+ if (total <= 0) return 0;
+ return clamp(value / total, 0, 1);
+}
+
+function ratioScore(r: number, maxScore: number): number {
+ if (r <= 0) return 0;
+ return Math.sqrt(r) * maxScore;
+}
+
+function buildExplanation(params: {
+ changedFiles: number;
+ changedSymbols: number;
+ changedEdges: number;
+ affectedNodes: number;
+ affectedRatio: number;
+ riskTags: string[];
+ entryPoints: number;
+ entryPointRatio: number;
+ impactedModules: number;
+ moduleSpreadRatio: number;
+ score: number;
+}): ImpactExplanation {
+ const contributors = [
+ {
+ factor: 'affectedNodes' as const,
+ value: params.affectedNodes,
+ weight: S_AFFECTED_RADIUS,
+ contribution: ratioScore(params.affectedRatio, S_AFFECTED_RADIUS),
+ },
+ {
+ factor: 'entryPoints' as const,
+ value: params.entryPoints,
+ weight: S_ENTRY_SURFACE,
+ contribution: ratioScore(params.entryPointRatio, S_ENTRY_SURFACE),
+ },
+ {
+ factor: 'changedFiles' as const,
+ value: params.impactedModules,
+ weight: S_MODULE_SPREAD,
+ contribution: ratioScore(params.moduleSpreadRatio, S_MODULE_SPREAD),
+ },
+ {
+ factor: 'changedEdges' as const,
+ value: params.changedEdges,
+ weight: S_STRUCTURAL_CHURN / 2,
+ contribution: normalize(params.changedEdges, S_STRUCTURAL_CHURN / 2, S_STRUCTURAL_CHURN / 2),
+ },
+ {
+ factor: 'changedSymbols' as const,
+ value: params.changedSymbols,
+ weight: S_STRUCTURAL_CHURN / 2,
+ contribution: normalize(params.changedSymbols, S_STRUCTURAL_CHURN / 2, S_STRUCTURAL_CHURN / 2),
+ },
+ {
+ factor: 'riskTags' as const,
+ value: params.riskTags.length,
+ weight: S_RISK_BONUS,
+ contribution: normalize(params.riskTags.length, S_RISK_BONUS, S_RISK_BONUS),
+ },
+ ].sort((a, b) => b.contribution - a.contribution);
+
+ const severity = severityFromScore(params.score);
+
+ const reasons: string[] = [];
+ reasons.push(
+ `blast radius: ${params.affectedNodes} nodes (~${Math.round(params.affectedRatio * 100)}% of indexed graph)`,
+ );
+ reasons.push(
+ `entry surface: ${params.entryPoints} entry points (~${Math.round(params.entryPointRatio * 100)}% of entry set)`,
+ );
+ reasons.push(
+ `module spread: ${params.impactedModules} modules (~${Math.round(params.moduleSpreadRatio * 100)}%)`,
+ );
+ reasons.push(`${params.changedSymbols} changed symbols, ${params.changedEdges} edge changes`);
+ if (params.riskTags.length > 0) {
+ reasons.push(`risk tags: ${params.riskTags.join(', ')}`);
+ }
+
+ return {
+ severity,
+ summary: `${severity.charAt(0).toUpperCase() + severity.slice(1)} impact based on structural change volume and risk surfaces.`,
+ reasons,
+ topContributors: contributors.slice(0, 4),
+ };
+}
+
+/**
+ * Deterministic impact score for a commit range (no LLM).
+ */
+export function computeImpactScore(
+ commitHash: string,
+ diff: GraphDiff,
+ head?: CodeGraphSnapshot,
+): ImpactSummary {
+ const changedSymbols =
+ diff.summary.symbolsAdded + diff.summary.symbolsRemoved + diff.summary.symbolsModified;
+ const changedEdges = diff.summary.edgesAdded + diff.summary.edgesRemoved;
+
+ const riskTags = collectRiskTags(diff);
+ const affectedSet = new Set(diff.affectedNodeIds);
+ const entryPoints = head ? impactedEntryPoints(head, affectedSet) : [];
+ const affectedNodeCount = diff.affectedNodeIds.length;
+ const totalNodes = head?.nodeCount ?? 0;
+ const affectedRatio = ratio(affectedNodeCount, totalNodes);
+
+ const allEntryPointCount = head
+ ? head.nodes.filter(
+ (node) =>
+ node.kind === 'route' ||
+ node.kind === 'component' ||
+ (node.isExported && (node.kind === 'function' || node.kind === 'method')),
+ ).length
+ : 0;
+ const entryPointRatio = ratio(entryPoints.length, allEntryPointCount);
+
+ const affectedModules = impactedModulesFromAffected(diff, head, affectedSet);
+ const totalModuleCount = head
+ ? new Set(head.nodes.map((node) => topLevelModule(node.filePath))).size
+ : affectedModules.length;
+ const moduleSpreadRatio = ratio(affectedModules.length, totalModuleCount);
+
+ const structuralChurn =
+ normalize(changedSymbols, S_STRUCTURAL_CHURN / 2, S_STRUCTURAL_CHURN / 2) +
+ normalize(changedEdges, S_STRUCTURAL_CHURN / 2, S_STRUCTURAL_CHURN / 2);
+
+ const raw =
+ ratioScore(affectedRatio, S_AFFECTED_RADIUS) +
+ ratioScore(entryPointRatio, S_ENTRY_SURFACE) +
+ ratioScore(moduleSpreadRatio, S_MODULE_SPREAD) +
+ structuralChurn +
+ normalize(riskTags.length, S_RISK_BONUS, S_RISK_BONUS);
+
+ const score = clamp(Math.round(raw), 0, 100);
+
+ return {
+ commitHash,
+ score,
+ changedSymbols,
+ changedEdges,
+ affectedModules,
+ impactedEntryPoints: entryPoints,
+ riskTags,
+ explanation: buildExplanation({
+ changedFiles: diff.changedFiles.length,
+ changedSymbols,
+ changedEdges,
+ affectedNodes: affectedNodeCount,
+ affectedRatio,
+ riskTags,
+ entryPoints: entryPoints.length,
+ entryPointRatio,
+ impactedModules: affectedModules.length,
+ moduleSpreadRatio,
+ score,
+ }),
+ };
+}
+
+export const __private = {
+ severityFromScore,
+};
diff --git a/packages/codedelta-impact-score/tsconfig.json b/packages/codedelta-impact-score/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-impact-score/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-provider-runtime/__tests__/codex-responses.test.ts b/packages/codedelta-provider-runtime/__tests__/codex-responses.test.ts
new file mode 100644
index 000000000..10f5b8e8f
--- /dev/null
+++ b/packages/codedelta-provider-runtime/__tests__/codex-responses.test.ts
@@ -0,0 +1,25 @@
+import { describe, expect, it } from 'vitest';
+import { extractResponsesText } from '../src/codex-responses';
+
+describe('extractResponsesText', () => {
+ it('reads output_text shortcut', () => {
+ expect(extractResponsesText({ output_text: ' hello ' })).toBe('hello');
+ });
+
+ it('reads message output blocks', () => {
+ expect(
+ extractResponsesText({
+ output: [
+ {
+ type: 'message',
+ content: [{ type: 'output_text', text: 'line one' }],
+ },
+ ],
+ }),
+ ).toBe('line one');
+ });
+
+ it('returns null for empty payload', () => {
+ expect(extractResponsesText({})).toBeNull();
+ });
+});
diff --git a/packages/codedelta-provider-runtime/__tests__/codex-stream.test.ts b/packages/codedelta-provider-runtime/__tests__/codex-stream.test.ts
new file mode 100644
index 000000000..dbfb500bb
--- /dev/null
+++ b/packages/codedelta-provider-runtime/__tests__/codex-stream.test.ts
@@ -0,0 +1,33 @@
+import { describe, expect, it } from 'vitest';
+import {
+ applyResponsesEvents,
+ finalizeResponsesText,
+ parseResponsesSseChunk,
+} from '../src/codex-stream';
+
+describe('codex-stream', () => {
+ it('parses delta events from SSE lines', () => {
+ const chunk =
+ 'data: {"type":"response.output_text.delta","delta":"Hello"}\n\n' +
+ 'data: {"type":"response.output_text.delta","delta":" world"}\n';
+ const { remainder, events } = parseResponsesSseChunk(chunk);
+ expect(remainder).toBe('');
+ const state = { streamed: '', completed: null };
+ applyResponsesEvents(events, state);
+ expect(finalizeResponsesText(state)).toBe('Hello world');
+ });
+
+ it('falls back to completed response payload', () => {
+ const state = { streamed: '', completed: null };
+ applyResponsesEvents(
+ [
+ {
+ type: 'response.completed',
+ response: { output_text: 'done' },
+ },
+ ],
+ state,
+ );
+ expect(finalizeResponsesText(state)).toBe('done');
+ });
+});
diff --git a/packages/codedelta-provider-runtime/package.json b/packages/codedelta-provider-runtime/package.json
new file mode 100644
index 000000000..22e9f29b8
--- /dev/null
+++ b/packages/codedelta-provider-runtime/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@codedelta/provider-runtime",
+ "version": "0.1.0",
+ "description": "LLM provider abstraction for CodeDelta",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/types": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-provider-runtime/src/codex-auth.ts b/packages/codedelta-provider-runtime/src/codex-auth.ts
new file mode 100644
index 000000000..18d32e87a
--- /dev/null
+++ b/packages/codedelta-provider-runtime/src/codex-auth.ts
@@ -0,0 +1,220 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import { fetchWithRetry, formatNetworkError } from './codex-http';
+
+const REFRESH_URL = 'https://auth.openai.com/oauth/token';
+const CODEX_OAUTH_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
+const REFRESH_SKEW_SECONDS = 30;
+
+export class CodexAuthError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'CodexAuthError';
+ }
+}
+
+export interface CodexAuthStatus {
+ configured: boolean;
+ authMode?: string;
+ codexHome: string;
+ authPath: string;
+ message: string;
+ defaultModel?: string;
+}
+
+export interface CodexCredentials {
+ accessToken: string;
+ accountId?: string;
+}
+
+export function getCodexHome(): string {
+ return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), '.codex');
+}
+
+export function getCodexAuthPath(): string {
+ return path.join(getCodexHome(), 'auth.json');
+}
+
+export function readCodexDefaultModel(): string | undefined {
+ const configPath = path.join(getCodexHome(), 'config.toml');
+ if (!fs.existsSync(configPath)) return undefined;
+ try {
+ const text = fs.readFileSync(configPath, 'utf8');
+ const match = text.match(/^model\s*=\s*"([^"]+)"/m);
+ return match?.[1];
+ } catch {
+ return undefined;
+ }
+}
+
+export function readCodexAuthStatus(): CodexAuthStatus {
+ const codexHome = getCodexHome();
+ const authPath = getCodexAuthPath();
+ const defaultModel = readCodexDefaultModel();
+
+ if (!fs.existsSync(authPath)) {
+ return {
+ configured: false,
+ codexHome,
+ authPath,
+ defaultModel,
+ message: `No auth file found at ${authPath}. Run codex login on this machine and try again.`,
+ };
+ }
+
+ try {
+ const data = JSON.parse(fs.readFileSync(authPath, 'utf8')) as {
+ auth_mode?: string;
+ tokens?: { access_token?: string; refresh_token?: string };
+ };
+ const authMode = data.auth_mode;
+ if (authMode !== 'chatgpt') {
+ return {
+ configured: false,
+ authMode,
+ codexHome,
+ authPath,
+ defaultModel,
+ message: `auth.json has auth_mode "${authMode ?? 'unknown'}". Only ChatGPT login mode ("chatgpt") is supported.`,
+ };
+ }
+ if (!data.tokens?.access_token) {
+ return {
+ configured: false,
+ authMode,
+ codexHome,
+ authPath,
+ defaultModel,
+ message: 'auth.json is missing access_token. Run codex login to re-authenticate.',
+ };
+ }
+ return {
+ configured: true,
+ authMode,
+ codexHome,
+ authPath,
+ defaultModel,
+ message: 'Local Codex CLI login detected and available for Trace View.',
+ };
+ } catch (err) {
+ return {
+ configured: false,
+ codexHome,
+ authPath,
+ defaultModel,
+ message: `Unable to read auth.json: ${err instanceof Error ? err.message : String(err)}`,
+ };
+ }
+}
+
+function jwtExp(token: string): number | null {
+ try {
+ const payloadB64 = token.split('.')[1];
+ if (!payloadB64) return null;
+ const padded = payloadB64 + '='.repeat((4 - (payloadB64.length % 4)) % 4);
+ const payload = JSON.parse(Buffer.from(padded, 'base64url').toString('utf8')) as { exp?: number };
+ return typeof payload.exp === 'number' ? payload.exp : null;
+ } catch {
+ return null;
+ }
+}
+
+async function refreshCodexTokens(refreshToken: string): Promise<{
+ access_token?: string;
+ id_token?: string;
+ refresh_token?: string;
+}> {
+ let res: Response;
+ try {
+ res = await fetchWithRetry(
+ REFRESH_URL,
+ {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ client_id: CODEX_OAUTH_CLIENT_ID,
+ grant_type: 'refresh_token',
+ refresh_token: refreshToken,
+ }),
+ },
+ { timeoutMs: 30_000, retries: 2 },
+ );
+ } catch (err) {
+ throw new CodexAuthError(formatNetworkError(err, 'Network failure while refreshing Codex auth token'));
+ }
+ const bodyText = await res.text();
+ if (!res.ok) {
+ let code: string | undefined;
+ try {
+ code = (JSON.parse(bodyText) as { error?: string }).error;
+ } catch {
+ /* ignore */
+ }
+ if (code === 'refresh_token_expired' || code === 'refresh_token_reused' || code === 'refresh_token_invalidated') {
+ throw new CodexAuthError(`Refresh token is no longer valid (${code}). Run codex login to re-authenticate.`);
+ }
+ throw new CodexAuthError(`Failed to refresh Codex token: HTTP ${res.status}`);
+ }
+ return JSON.parse(bodyText) as {
+ access_token?: string;
+ id_token?: string;
+ refresh_token?: string;
+ };
+}
+
+function writeAuthFile(authPath: string, data: Record): void {
+ const tmp = `${authPath}.tmp`;
+ fs.writeFileSync(tmp, `${JSON.stringify(data, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
+ fs.renameSync(tmp, authPath);
+ fs.chmodSync(authPath, 0o600);
+}
+
+/** Borrow ChatGPT OAuth credentials from local Codex CLI cache (refreshes when near expiry). */
+export async function borrowCodexCredentials(): Promise {
+ const status = readCodexAuthStatus();
+ if (!status.configured) {
+ throw new CodexAuthError(status.message);
+ }
+
+ const authPath = getCodexAuthPath();
+ const data = JSON.parse(fs.readFileSync(authPath, 'utf8')) as {
+ auth_mode?: string;
+ tokens: {
+ access_token: string;
+ refresh_token?: string;
+ account_id?: string;
+ id_token?: string;
+ };
+ last_refresh?: string;
+ };
+
+ if (data.auth_mode !== 'chatgpt') {
+ throw new CodexAuthError(`Unsupported auth_mode: ${data.auth_mode ?? 'unknown'}`);
+ }
+
+ const tokens = data.tokens;
+ let accessToken = tokens.access_token;
+ const accountId = tokens.account_id;
+ const exp = jwtExp(accessToken);
+
+ if (exp !== null && Date.now() / 1000 < exp - REFRESH_SKEW_SECONDS) {
+ return { accessToken, accountId };
+ }
+
+ const refreshToken = tokens.refresh_token;
+ if (!refreshToken) {
+ throw new CodexAuthError('access_token expired and refresh_token is missing. Run codex login.');
+ }
+
+ const newTokens = await refreshCodexTokens(refreshToken);
+ if (newTokens.access_token) tokens.access_token = newTokens.access_token;
+ if (newTokens.id_token) tokens.id_token = newTokens.id_token;
+ if (newTokens.refresh_token) tokens.refresh_token = newTokens.refresh_token;
+ data.tokens = tokens;
+ data.last_refresh = new Date().toISOString();
+ writeAuthFile(authPath, data as Record);
+ accessToken = tokens.access_token;
+
+ return { accessToken, accountId };
+}
diff --git a/packages/codedelta-provider-runtime/src/codex-http.ts b/packages/codedelta-provider-runtime/src/codex-http.ts
new file mode 100644
index 000000000..87188cd4e
--- /dev/null
+++ b/packages/codedelta-provider-runtime/src/codex-http.ts
@@ -0,0 +1,60 @@
+const CODEX_REQUEST_TIMEOUT_MS = 120_000;
+const CODEX_MAX_RETRIES = 2;
+
+function sleep(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+/** Surface undici/Node fetch root cause (often hidden behind "fetch failed"). */
+export function formatNetworkError(err: unknown, context: string): string {
+ if (!(err instanceof Error)) {
+ return `${context}: ${String(err)}`;
+ }
+ const parts = [err.message || 'fetch failed'];
+ const cause = err.cause;
+ if (cause instanceof Error) {
+ parts.push(cause.message);
+ const code = (cause as NodeJS.ErrnoException).code;
+ if (code) parts.push(`(${code})`);
+ }
+ return `${context}: ${parts.join(' — ')}`;
+}
+
+async function fetchOnce(url: string, init: RequestInit, timeoutMs: number): Promise {
+ const controller = new AbortController();
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
+ try {
+ return await fetch(url, { ...init, signal: controller.signal });
+ } catch (err) {
+ if (err instanceof Error && err.name === 'AbortError') {
+ throw new Error(`${formatNetworkError(err, 'Request timed out')} (${timeoutMs / 1000}s)`);
+ }
+ throw new Error(formatNetworkError(err, 'Network request failed'));
+ } finally {
+ clearTimeout(timer);
+ }
+}
+
+/** Fetch with timeout + one retry for transient Codex/network failures. */
+export async function fetchWithRetry(
+ url: string,
+ init: RequestInit,
+ options?: { timeoutMs?: number; retries?: number },
+): Promise {
+ const timeoutMs = options?.timeoutMs ?? CODEX_REQUEST_TIMEOUT_MS;
+ const retries = options?.retries ?? CODEX_MAX_RETRIES;
+ let lastError: Error | null = null;
+
+ for (let attempt = 0; attempt < retries; attempt++) {
+ try {
+ return await fetchOnce(url, init, timeoutMs);
+ } catch (err) {
+ lastError = err instanceof Error ? err : new Error(String(err));
+ if (attempt < retries - 1) {
+ await sleep(400 * (attempt + 1));
+ }
+ }
+ }
+
+ throw lastError ?? new Error('Network request failed');
+}
diff --git a/packages/codedelta-provider-runtime/src/codex-responses.ts b/packages/codedelta-provider-runtime/src/codex-responses.ts
new file mode 100644
index 000000000..d69d6ca78
--- /dev/null
+++ b/packages/codedelta-provider-runtime/src/codex-responses.ts
@@ -0,0 +1,29 @@
+/** Codex ChatGPT backend (same surface as Codex CLI). */
+export const CODEX_BACKEND_BASE_URL = 'https://chatgpt.com/backend-api/codex';
+
+export function extractResponsesText(json: unknown): string | null {
+ if (!json || typeof json !== 'object') return null;
+ const root = json as Record;
+
+ if (typeof root.output_text === 'string' && root.output_text.trim()) {
+ return root.output_text.trim();
+ }
+
+ const output = root.output;
+ if (!Array.isArray(output)) return null;
+
+ const parts: string[] = [];
+ for (const item of output) {
+ if (!item || typeof item !== 'object') continue;
+ const row = item as Record;
+ if (row.type !== 'message' || !Array.isArray(row.content)) continue;
+ for (const block of row.content) {
+ if (!block || typeof block !== 'object') continue;
+ const b = block as Record;
+ if (b.type === 'output_text' && typeof b.text === 'string' && b.text.trim()) {
+ parts.push(b.text.trim());
+ }
+ }
+ }
+ return parts.length > 0 ? parts.join('\n').trim() : null;
+}
diff --git a/packages/codedelta-provider-runtime/src/codex-stream.ts b/packages/codedelta-provider-runtime/src/codex-stream.ts
new file mode 100644
index 000000000..1bf07247e
--- /dev/null
+++ b/packages/codedelta-provider-runtime/src/codex-stream.ts
@@ -0,0 +1,74 @@
+import { extractResponsesText } from './codex-responses';
+
+/** Parse Codex /responses SSE (stream must be true). */
+export function parseResponsesSseChunk(buffer: string): {
+ remainder: string;
+ events: unknown[];
+} {
+ const events: unknown[] = [];
+ const lines = buffer.split('\n');
+ const remainder = lines.pop() ?? '';
+ for (const line of lines) {
+ const trimmed = line.trim();
+ if (!trimmed.startsWith('data:')) continue;
+ const payload = trimmed.replace(/^data:\s*/, '');
+ if (!payload || payload === '[DONE]') continue;
+ try {
+ events.push(JSON.parse(payload));
+ } catch {
+ /* skip malformed chunk */
+ }
+ }
+ return { remainder, events };
+}
+
+export function applyResponsesEvents(
+ events: unknown[],
+ state: { streamed: string; completed: unknown | null },
+): void {
+ for (const event of events) {
+ if (!event || typeof event !== 'object') continue;
+ const e = event as Record;
+ if (e.type === 'response.output_text.delta' && typeof e.delta === 'string') {
+ state.streamed += e.delta;
+ } else if (e.type === 'response.completed' && e.response) {
+ state.completed = e.response;
+ }
+ }
+}
+
+export function finalizeResponsesText(state: {
+ streamed: string;
+ completed: unknown | null;
+}): string {
+ const streamedTrimmed = state.streamed.trim();
+ if (streamedTrimmed) return streamedTrimmed;
+ if (state.completed) {
+ const fromCompleted = extractResponsesText(state.completed);
+ if (fromCompleted) return fromCompleted;
+ }
+ return '';
+}
+
+export async function readResponsesSseStream(body: ReadableStream): Promise {
+ const reader = body.getReader();
+ const decoder = new TextDecoder();
+ let buffer = '';
+ const state = { streamed: '', completed: null as unknown | null };
+
+ for (;;) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ buffer += decoder.decode(value, { stream: true });
+ const parsed = parseResponsesSseChunk(buffer);
+ buffer = parsed.remainder;
+ applyResponsesEvents(parsed.events, state);
+ }
+
+ if (buffer.trim()) {
+ const parsed = parseResponsesSseChunk(`${buffer}\n`);
+ applyResponsesEvents(parsed.events, state);
+ }
+
+ return finalizeResponsesText(state);
+}
diff --git a/packages/codedelta-provider-runtime/src/index.ts b/packages/codedelta-provider-runtime/src/index.ts
new file mode 100644
index 000000000..f16a26b0f
--- /dev/null
+++ b/packages/codedelta-provider-runtime/src/index.ts
@@ -0,0 +1,254 @@
+import type { ModelProviderConfig } from '@codedelta/types';
+import { borrowCodexCredentials, CodexAuthError, readCodexAuthStatus, readCodexDefaultModel } from './codex-auth';
+import { fetchWithRetry } from './codex-http';
+import { CODEX_BACKEND_BASE_URL, extractResponsesText } from './codex-responses';
+import { readResponsesSseStream } from './codex-stream';
+
+export { borrowCodexCredentials, CodexAuthError, readCodexAuthStatus, readCodexDefaultModel };
+export { CODEX_BACKEND_BASE_URL, extractResponsesText };
+
+export interface ChatMessage {
+ role: 'user' | 'assistant';
+ content: string;
+}
+
+export interface ChatCompleteInput {
+ system: string;
+ messages: ChatMessage[];
+ temperature?: number;
+}
+
+export interface ChatProvider {
+ readonly id: string;
+ readonly label: string;
+ isConfigured(): boolean;
+ complete(input: ChatCompleteInput): Promise;
+}
+
+/** No-AI provider — returns a deterministic message explaining AI is disabled. */
+export class NoAiProvider implements ChatProvider {
+ readonly id = 'none';
+ readonly label = 'No AI';
+
+ isConfigured(): boolean {
+ return true;
+ }
+
+ async complete(_input: ChatCompleteInput): Promise {
+ return 'No-AI mode: deterministic candidate and evidence output only.';
+ }
+}
+
+class OpenAiLikeProvider implements ChatProvider {
+ readonly id: 'openai' | 'openai-compatible';
+ readonly label: string;
+ private readonly config: ModelProviderConfig;
+
+ constructor(id: 'openai' | 'openai-compatible', config: ModelProviderConfig) {
+ this.id = id;
+ this.label = id === 'openai' ? 'OpenAI' : 'OpenAI-compatible';
+ this.config = config;
+ }
+
+ isConfigured(): boolean {
+ return Boolean(this.config.apiKey && (this.config.baseUrl || this.id === 'openai'));
+ }
+
+ async complete(input: ChatCompleteInput): Promise {
+ if (!this.isConfigured()) {
+ throw new Error(`${this.label} provider is not configured`);
+ }
+ const baseUrl =
+ this.id === 'openai'
+ ? (this.config.baseUrl ?? 'https://api.openai.com/v1')
+ : (this.config.baseUrl as string);
+ const model = this.config.model ?? 'gpt-4o-mini';
+ const res = await fetchWithRetry(
+ `${baseUrl.replace(/\/$/, '')}/chat/completions`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${this.config.apiKey as string}`,
+ },
+ body: JSON.stringify({
+ model,
+ temperature: input.temperature ?? 0.1,
+ messages: [
+ { role: 'system', content: input.system },
+ ...input.messages.map((m) => ({ role: m.role, content: m.content })),
+ ],
+ }),
+ },
+ { timeoutMs: 120_000, retries: 2 },
+ );
+ if (!res.ok) {
+ throw new Error(`${this.label} provider failed: HTTP ${res.status}`);
+ }
+ const json = (await res.json()) as {
+ choices?: Array<{ message?: { content?: string } }>;
+ };
+ const content = json.choices?.[0]?.message?.content?.trim();
+ if (!content) {
+ throw new Error(`${this.label} provider returned empty response`);
+ }
+ return content;
+ }
+}
+
+/** Phase 3 TODO: OpenAI API provider. */
+export class OpenAiProvider implements ChatProvider {
+ readonly id = 'openai';
+ readonly label = 'OpenAI';
+ private readonly inner: OpenAiLikeProvider;
+ constructor(config: ModelProviderConfig) {
+ this.inner = new OpenAiLikeProvider('openai', config);
+ }
+ isConfigured(): boolean {
+ return this.inner.isConfigured();
+ }
+ async complete(input: ChatCompleteInput): Promise {
+ return this.inner.complete(input);
+ }
+}
+
+/** Phase 3 TODO: Anthropic provider. */
+export class AnthropicProvider implements ChatProvider {
+ readonly id = 'anthropic';
+ readonly label = 'Anthropic';
+ constructor(_config: ModelProviderConfig) {}
+ isConfigured(): boolean {
+ return false;
+ }
+ async complete(_input: ChatCompleteInput): Promise {
+ throw new Error('Anthropic provider: not implemented (Phase 3)');
+ }
+}
+
+/** Phase 3 TODO: Ollama provider. */
+export class OllamaProvider implements ChatProvider {
+ readonly id = 'ollama';
+ readonly label = 'Ollama';
+ constructor(_config: ModelProviderConfig) {}
+ isConfigured(): boolean {
+ return false;
+ }
+ async complete(_input: ChatCompleteInput): Promise {
+ throw new Error('Ollama provider: not implemented (Phase 3)');
+ }
+}
+
+/** Phase 3 TODO: OpenAI-compatible endpoint provider. */
+export class OpenAiCompatibleProvider implements ChatProvider {
+ readonly id = 'openai-compatible';
+ readonly label = 'OpenAI-compatible';
+ private readonly inner: OpenAiLikeProvider;
+ constructor(config: ModelProviderConfig) {
+ this.inner = new OpenAiLikeProvider('openai-compatible', config);
+ }
+ isConfigured(): boolean {
+ return this.inner.isConfigured();
+ }
+ async complete(input: ChatCompleteInput): Promise {
+ return this.inner.complete(input);
+ }
+}
+
+/** Reuse local Codex CLI ChatGPT OAuth credentials (~/.codex/auth.json). */
+export class CodexOAuthProvider implements ChatProvider {
+ readonly id = 'codex-oauth';
+ readonly label = 'Codex OAuth';
+ private readonly config: ModelProviderConfig;
+
+ constructor(config: ModelProviderConfig) {
+ this.config = config;
+ }
+
+ isConfigured(): boolean {
+ return readCodexAuthStatus().configured;
+ }
+
+ async complete(input: ChatCompleteInput): Promise {
+ const { accessToken, accountId } = await borrowCodexCredentials();
+ const model = this.config.model ?? readCodexDefaultModel() ?? 'gpt-4o-mini';
+ const inputMessages = input.messages.map((m) => ({
+ role: m.role,
+ content: m.content,
+ }));
+
+ let res: Response;
+ try {
+ res = await fetchWithRetry(
+ `${CODEX_BACKEND_BASE_URL}/responses`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`,
+ ...(accountId ? { 'ChatGPT-Account-ID': accountId } : {}),
+ },
+ // Codex ChatGPT backend rejects `temperature` (differs from OpenAI API).
+ body: JSON.stringify({
+ model,
+ input: inputMessages,
+ instructions: input.system,
+ store: false,
+ stream: true,
+ }),
+ },
+ { timeoutMs: 120_000, retries: 2 },
+ );
+ } catch (err) {
+ throw new Error(
+ err instanceof Error
+ ? `Codex OAuth provider failed: ${err.message}`
+ : 'Codex OAuth provider failed: network error',
+ );
+ }
+
+ if (!res.ok) {
+ const detail = await res.text().catch(() => '');
+ throw new Error(
+ `Codex OAuth provider failed: HTTP ${res.status}${detail ? ` — ${detail.slice(0, 200)}` : ''}`,
+ );
+ }
+
+ if (!res.body) {
+ throw new Error('Codex OAuth provider returned empty body');
+ }
+
+ let content: string;
+ try {
+ content = await readResponsesSseStream(res.body);
+ } catch (err) {
+ throw new Error(
+ err instanceof Error
+ ? `Codex OAuth provider failed while reading stream: ${err.message}`
+ : 'Codex OAuth provider failed while reading stream',
+ );
+ }
+ if (!content) {
+ throw new Error('Codex OAuth provider returned empty response');
+ }
+ return content;
+ }
+}
+
+export function createProvider(config: ModelProviderConfig): ChatProvider {
+ switch (config.kind) {
+ case 'none':
+ return new NoAiProvider();
+ case 'openai':
+ return new OpenAiProvider(config);
+ case 'anthropic':
+ return new AnthropicProvider(config);
+ case 'ollama':
+ return new OllamaProvider(config);
+ case 'openai-compatible':
+ return new OpenAiCompatibleProvider(config);
+ case 'codex-oauth':
+ return new CodexOAuthProvider(config);
+ default:
+ return new NoAiProvider();
+ }
+}
diff --git a/packages/codedelta-provider-runtime/tsconfig.json b/packages/codedelta-provider-runtime/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-provider-runtime/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-repo-manager/__tests__/repo-manager.test.ts b/packages/codedelta-repo-manager/__tests__/repo-manager.test.ts
new file mode 100644
index 000000000..0c611f15b
--- /dev/null
+++ b/packages/codedelta-repo-manager/__tests__/repo-manager.test.ts
@@ -0,0 +1,86 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import { execFileSync } from 'child_process';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import {
+ computeRepoId,
+ getChangedFiles,
+ getCommit,
+ importLocalRepo,
+ listBranches,
+ listCommits,
+ parseGitHubInput,
+} from '../src';
+
+function run(cmd: string, cwd: string): void {
+ execFileSync('sh', ['-c', cmd], { cwd, stdio: 'pipe' });
+}
+
+describe('codedelta-repo-manager (pure)', () => {
+ it('parses GitHub URLs and owner/repo shorthand', () => {
+ const a = parseGitHubInput('https://github.com/foo/bar');
+ expect(a.owner).toBe('foo');
+ expect(a.name).toBe('bar');
+ expect(a.normalizedInput).toBe('https://github.com/foo/bar');
+
+ const b = parseGitHubInput('foo/bar');
+ expect(b.cloneUrl).toBe('https://github.com/foo/bar.git');
+ });
+
+ it('computes stable repo ids', () => {
+ const id1 = computeRepoId('https://github.com/foo/bar');
+ const id2 = computeRepoId('https://github.com/foo/bar');
+ expect(id1).toBe(id2);
+ expect(id1).toHaveLength(16);
+ });
+});
+
+describe('codedelta-repo-manager (git)', () => {
+ let tmpDir: string;
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-repo-'));
+ run('git init -b main', tmpDir);
+ run('git config user.email "test@example.com"', tmpDir);
+ run('git config user.name "Test User"', tmpDir);
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), '# test\n');
+ run('git add README.md && git commit -m "initial commit"', tmpDir);
+ fs.writeFileSync(path.join(tmpDir, 'src.ts'), 'export const x = 1;\n');
+ run('git add src.ts && git commit -m "add src module"', tmpDir);
+ });
+
+ afterEach(() => {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('imports a local repo and lists branches', () => {
+ const ref = importLocalRepo(tmpDir);
+ expect(ref.source).toBe('local');
+ expect(ref.clonePath).toBe(fs.realpathSync(tmpDir));
+ expect(ref.defaultBranch).toBe('main');
+
+ const branches = listBranches(ref.clonePath);
+ expect(branches).toContain('main');
+ });
+
+ it('lists commits with changed file counts', () => {
+ const ref = importLocalRepo(tmpDir);
+ const commits = listCommits(ref.clonePath, 'main', { limit: 10 });
+ expect(commits.length).toBe(2);
+ expect(commits[0]?.message).toBe('add src module');
+ expect(commits[0]?.changedFilesCount).toBeGreaterThan(0);
+ expect(commits[1]?.message).toBe('initial commit');
+ });
+
+ it('returns changed files for a commit', () => {
+ const ref = importLocalRepo(tmpDir);
+ const commits = listCommits(ref.clonePath, 'main', { limit: 1 });
+ const hash = commits[0]!.hash;
+ const commit = getCommit(ref.clonePath, hash);
+ expect(commit.hash).toBe(hash);
+
+ const files = getChangedFiles(ref.clonePath, hash);
+ expect(files.some((f) => f.path === 'src.ts')).toBe(true);
+ });
+});
diff --git a/packages/codedelta-repo-manager/package.json b/packages/codedelta-repo-manager/package.json
new file mode 100644
index 000000000..d85d940d2
--- /dev/null
+++ b/packages/codedelta-repo-manager/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "@codedelta/repo-manager",
+ "version": "0.1.0",
+ "description": "Git repository import and commit operations for CodeDelta",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/types": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-repo-manager/src/cache-layout.ts b/packages/codedelta-repo-manager/src/cache-layout.ts
new file mode 100644
index 000000000..c906df435
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/cache-layout.ts
@@ -0,0 +1,45 @@
+import * as path from 'path';
+import * as os from 'os';
+
+const DEFAULT_CACHE_DIR = '.codedelta';
+
+/** Resolve the CodeDelta cache root directory. */
+export function getCacheRoot(cwd: string = process.cwd()): string {
+ const env = process.env.CODEDELTA_CACHE_DIR;
+ if (env) {
+ return path.isAbsolute(env) ? env : path.resolve(cwd, env);
+ }
+ return path.resolve(cwd, DEFAULT_CACHE_DIR);
+}
+
+export function getReposDir(cacheRoot: string): string {
+ return path.join(cacheRoot, 'repos');
+}
+
+export function getRepoClonePath(cacheRoot: string, repoId: string): string {
+ return path.join(getReposDir(cacheRoot), repoId);
+}
+
+export function getSnapshotsDir(cacheRoot: string): string {
+ return path.join(cacheRoot, 'snapshots');
+}
+
+export function getRegistryPath(cacheRoot: string): string {
+ return path.join(cacheRoot, 'registry.json');
+}
+
+export function getSettingsPath(cacheRoot: string): string {
+ return path.join(cacheRoot, 'settings.json');
+}
+
+export function getWorktreesDir(cacheRoot: string, repoId: string): string {
+ return path.join(cacheRoot, 'worktrees', repoId);
+}
+
+/** Expand ~ in paths on POSIX systems. */
+export function expandHome(input: string): string {
+ if (input.startsWith('~/') || input === '~') {
+ return path.join(os.homedir(), input.slice(1));
+ }
+ return input;
+}
diff --git a/packages/codedelta-repo-manager/src/commits.ts b/packages/codedelta-repo-manager/src/commits.ts
new file mode 100644
index 000000000..c7785089a
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/commits.ts
@@ -0,0 +1,204 @@
+import type { ChangedFile, CommitDetail, CommitInfo } from '@codedelta/types';
+import { git } from './git-runner';
+
+export interface ListCommitsOptions {
+ limit?: number;
+ skip?: number;
+}
+
+interface GitLogEntry {
+ commit: string;
+ parents: string;
+ author: string;
+ authorEmail: string;
+ date: string;
+ subject: string;
+}
+
+/** Resolve branch/ref to a revision git accepts. */
+export function resolveRef(clonePath: string, branch: string): string {
+ // Bare repos use refs/heads/* directly
+ try {
+ return git(['rev-parse', '--verify', branch], { cwd: clonePath });
+ } catch {
+ return git(['rev-parse', '--verify', `origin/${branch}`], { cwd: clonePath });
+ }
+}
+
+/** List commits for a branch with pagination. */
+export function listCommits(
+ clonePath: string,
+ branch: string,
+ options: ListCommitsOptions = {},
+): CommitInfo[] {
+ const limit = options.limit ?? 50;
+ const skip = options.skip ?? 0;
+ const ref = resolveRef(clonePath, branch);
+
+ const format = [
+ '%H',
+ '%P',
+ '%an',
+ '%ae',
+ '%aI',
+ '%s',
+ ].join('%x1f');
+
+ const args = [
+ 'log',
+ ref,
+ `--max-count=${limit}`,
+ `--skip=${skip}`,
+ `--format=${format}`,
+ ];
+
+ const output = git(args, { cwd: clonePath });
+ if (!output) return [];
+
+ const entries: GitLogEntry[] = output.split('\n').filter(Boolean).map((line) => {
+ const [commit, parents, author, authorEmail, date, subject] = line.split('\x1f');
+ return {
+ commit: commit ?? '',
+ parents: parents ?? '',
+ author: author ?? '',
+ authorEmail: authorEmail ?? '',
+ date: date ?? '',
+ subject: subject ?? '',
+ };
+ });
+
+ return entries.map((e) => {
+ const changedFilesCount = countChangedFiles(clonePath, e.commit);
+ return {
+ hash: e.commit,
+ shortHash: e.commit.slice(0, 7),
+ message: e.subject,
+ author: e.author,
+ authorEmail: e.authorEmail,
+ date: e.date,
+ parents: e.parents ? e.parents.split(' ').filter(Boolean) : [],
+ changedFilesCount,
+ };
+ });
+}
+
+/** Get metadata for a single commit. */
+export function getCommit(clonePath: string, hash: string): CommitInfo {
+ const format = ['%H', '%P', '%an', '%ae', '%aI', '%s'].join('%x1f');
+ const line = git(['show', '-s', `--format=${format}`, hash], { cwd: clonePath });
+ const [commit, parents, author, authorEmail, date, subject] = line.split('\x1f');
+
+ return {
+ hash: commit ?? hash,
+ shortHash: (commit ?? hash).slice(0, 7),
+ message: subject ?? '',
+ author: author ?? '',
+ authorEmail: authorEmail ?? '',
+ date: date ?? '',
+ parents: parents ? parents.split(' ').filter(Boolean) : [],
+ changedFilesCount: countChangedFiles(clonePath, commit ?? hash),
+ };
+}
+
+/** Get commit with changed files list. */
+export function getCommitDetail(clonePath: string, hash: string): CommitDetail {
+ const commit = getCommit(clonePath, hash);
+ const changedFiles = getChangedFiles(clonePath, hash);
+ return { ...commit, changedFiles };
+}
+
+/** Count files changed in a commit (root commits return 0). */
+export function countChangedFiles(clonePath: string, hash: string): number {
+ try {
+ const out = git(
+ ['diff-tree', '--no-commit-id', '-r', '--name-only', hash],
+ { cwd: clonePath },
+ );
+ if (!out) return 0;
+ return out.split('\n').filter(Boolean).length;
+ } catch {
+ return 0;
+ }
+}
+
+const STATUS_MAP: Record = {
+ A: 'added',
+ M: 'modified',
+ D: 'deleted',
+ R: 'renamed',
+ C: 'copied',
+ T: 'modified',
+};
+
+/** List files changed in a single commit. */
+export function getChangedFiles(clonePath: string, hash: string): ChangedFile[] {
+ try {
+ const out = git(
+ ['diff-tree', '--no-commit-id', '-r', '-M', '--name-status', hash],
+ { cwd: clonePath },
+ );
+ if (!out) return [];
+ return parseNameStatus(out);
+ } catch {
+ return [];
+ }
+}
+
+/** List files changed between two commits. */
+export function getChangedFilesForRange(
+ clonePath: string,
+ base: string,
+ head: string,
+): ChangedFile[] {
+ const out = git(['diff', '--name-status', `${base}..${head}`], { cwd: clonePath });
+ if (!out) return [];
+ return parseNameStatus(out);
+}
+
+function parseNameStatus(output: string): ChangedFile[] {
+ const files: ChangedFile[] = [];
+ for (const line of output.split('\n').filter(Boolean)) {
+ const parts = line.split('\t');
+ const statusCode = parts[0]?.charAt(0) ?? 'M';
+ const status = STATUS_MAP[statusCode] ?? 'modified';
+
+ if (status === 'renamed' || status === 'copied') {
+ const oldPath = parts[1];
+ const newPath = parts[2];
+ if (newPath) {
+ files.push({ path: newPath, status, oldPath });
+ }
+ } else if (parts[1]) {
+ files.push({ path: parts[1], status });
+ }
+ }
+ return files;
+}
+
+/** List branch names (local + remote, deduplicated). */
+export function listBranches(clonePath: string): string[] {
+ const branches = new Set();
+
+ try {
+ const local = git(['branch', '--format=%(refname:short)'], { cwd: clonePath });
+ for (const b of local.split('\n').filter(Boolean)) {
+ branches.add(b);
+ }
+ } catch {
+ // bare repo may not have local branches named simply
+ }
+
+ try {
+ const remote = git(['branch', '-r', '--format=%(refname:short)'], { cwd: clonePath });
+ for (const b of remote.split('\n').filter(Boolean)) {
+ const name = b.replace(/^origin\//, '');
+ if (name !== 'HEAD' && !name.includes('HEAD')) {
+ branches.add(name);
+ }
+ }
+ } catch {
+ // ignore
+ }
+
+ return Array.from(branches).sort();
+}
diff --git a/packages/codedelta-repo-manager/src/git-runner.ts b/packages/codedelta-repo-manager/src/git-runner.ts
new file mode 100644
index 000000000..eb23ad4dc
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/git-runner.ts
@@ -0,0 +1,85 @@
+import { execFileSync } from 'child_process';
+
+export class GitCommandError extends Error {
+ constructor(
+ message: string,
+ public readonly command: string[],
+ public readonly stderr: string,
+ ) {
+ super(message);
+ this.name = 'GitCommandError';
+ }
+}
+
+export class RepoNotFoundError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'RepoNotFoundError';
+ }
+}
+
+export class InvalidGitHubUrlError extends Error {
+ constructor(message: string) {
+ super(message);
+ this.name = 'InvalidGitHubUrlError';
+ }
+}
+
+export interface GitRunOptions {
+ cwd: string;
+ /** When true, stderr is included in thrown GitCommandError. */
+ captureStderr?: boolean;
+}
+
+/** Run a git command and return stdout trimmed. */
+export function git(args: string[], options: GitRunOptions): string {
+ try {
+ const out = execFileSync('git', args, {
+ cwd: options.cwd,
+ encoding: 'utf8',
+ stdio: ['ignore', 'pipe', options.captureStderr ? 'pipe' : 'ignore'],
+ maxBuffer: 50 * 1024 * 1024,
+ });
+ return out.trim();
+ } catch (err: unknown) {
+ const e = err as { stderr?: string; message?: string };
+ const stderr = e.stderr?.toString?.() ?? '';
+ throw new GitCommandError(
+ `git ${args.join(' ')} failed: ${stderr || e.message || 'unknown error'}`,
+ ['git', ...args],
+ stderr,
+ );
+ }
+}
+
+/** Return true when path is inside a git working tree. */
+export function isGitRepo(dir: string): boolean {
+ try {
+ git(['rev-parse', '--is-inside-work-tree'], { cwd: dir });
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+/** Absolute path to the git working tree root. */
+export function gitRoot(dir: string): string {
+ return git(['rev-parse', '--show-toplevel'], { cwd: dir });
+}
+
+/** Default branch name for a clone. */
+export function getDefaultBranch(clonePath: string): string {
+ try {
+ const symref = git(['symbolic-ref', 'refs/remotes/origin/HEAD'], { cwd: clonePath });
+ const match = symref.match(/refs\/remotes\/origin\/(.+)/);
+ if (match?.[1]) return match[1];
+ } catch {
+ // fall through
+ }
+
+ try {
+ return git(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: clonePath });
+ } catch {
+ return 'main';
+ }
+}
diff --git a/packages/codedelta-repo-manager/src/github-clone.ts b/packages/codedelta-repo-manager/src/github-clone.ts
new file mode 100644
index 000000000..57d3cbb59
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/github-clone.ts
@@ -0,0 +1,121 @@
+import * as crypto from 'crypto';
+import * as fs from 'fs';
+import * as path from 'path';
+import type { RepoRef } from '@codedelta/types';
+import { getCacheRoot, getRepoClonePath, getReposDir } from './cache-layout';
+import { git, getDefaultBranch, InvalidGitHubUrlError } from './git-runner';
+
+const GITHUB_URL_RE =
+ /^https?:\/\/(?:www\.)?github\.com\/([^/]+)\/([^/#?]+?)(?:\.git)?(?:[/?#].*)?$/i;
+
+const GITHUB_SHORT_RE = /^([^/]+\/[^/]+)$/;
+
+export interface ParsedGitHubRepo {
+ owner: string;
+ name: string;
+ cloneUrl: string;
+ normalizedInput: string;
+}
+
+/** Parse GitHub URL or owner/repo shorthand into clone URL. */
+export function parseGitHubInput(input: string): ParsedGitHubRepo {
+ const trimmed = input.trim();
+ const urlMatch = trimmed.match(GITHUB_URL_RE);
+ if (urlMatch) {
+ const owner = urlMatch[1]!;
+ const name = urlMatch[2]!.replace(/\.git$/, '');
+ return {
+ owner,
+ name,
+ cloneUrl: `https://github.com/${owner}/${name}.git`,
+ normalizedInput: `https://github.com/${owner}/${name}`,
+ };
+ }
+
+ const shortMatch = trimmed.match(GITHUB_SHORT_RE);
+ if (shortMatch) {
+ const [owner, name] = shortMatch[1]!.split('/');
+ if (owner && name) {
+ return {
+ owner,
+ name,
+ cloneUrl: `https://github.com/${owner}/${name}.git`,
+ normalizedInput: `https://github.com/${owner}/${name}`,
+ };
+ }
+ }
+
+ throw new InvalidGitHubUrlError(
+ `Invalid GitHub URL or owner/repo: "${input}". Expected https://github.com/owner/repo or owner/repo.`,
+ );
+}
+
+/** Stable repo id from normalized source string. */
+export function computeRepoId(normalizedSource: string): string {
+ return crypto.createHash('sha256').update(normalizedSource).digest('hex').slice(0, 16);
+}
+
+export interface ImportGitHubOptions {
+ cacheRoot?: string;
+ /** When true, skip clone if directory already exists. */
+ reuseExisting?: boolean;
+}
+
+/** Clone a public GitHub repository into the CodeDelta cache. */
+export function importGitHubRepo(
+ url: string,
+ options: ImportGitHubOptions = {},
+): RepoRef {
+ const parsed = parseGitHubInput(url);
+ const cacheRoot = options.cacheRoot ?? getCacheRoot();
+ const repoId = computeRepoId(parsed.normalizedInput);
+ const clonePath = getRepoClonePath(cacheRoot, repoId);
+
+ fs.mkdirSync(getReposDir(cacheRoot), { recursive: true });
+
+ if (options.reuseExisting !== false && fs.existsSync(clonePath)) {
+ if (isValidGitDir(clonePath)) {
+ return buildRepoRef({
+ id: repoId,
+ source: 'github',
+ input: url.trim(),
+ clonePath,
+ remoteUrl: parsed.cloneUrl,
+ });
+ }
+ fs.rmSync(clonePath, { recursive: true, force: true });
+ }
+
+ git(['clone', '--bare', parsed.cloneUrl, clonePath], { cwd: cacheRoot, captureStderr: true });
+
+ return buildRepoRef({
+ id: repoId,
+ source: 'github',
+ input: url.trim(),
+ clonePath,
+ remoteUrl: parsed.cloneUrl,
+ });
+}
+
+function isValidGitDir(dir: string): boolean {
+ return fs.existsSync(path.join(dir, 'HEAD'));
+}
+
+function buildRepoRef(params: {
+ id: string;
+ source: 'github' | 'local';
+ input: string;
+ clonePath: string;
+ remoteUrl?: string;
+}): RepoRef {
+ const defaultBranch = getDefaultBranch(params.clonePath);
+ return {
+ id: params.id,
+ source: params.source,
+ input: params.input,
+ clonePath: params.clonePath,
+ defaultBranch,
+ remoteUrl: params.remoteUrl,
+ importedAt: new Date().toISOString(),
+ };
+}
diff --git a/packages/codedelta-repo-manager/src/index.ts b/packages/codedelta-repo-manager/src/index.ts
new file mode 100644
index 000000000..2d1ffacd6
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/index.ts
@@ -0,0 +1,49 @@
+export {
+ getCacheRoot,
+ getReposDir,
+ getRepoClonePath,
+ getSnapshotsDir,
+ getRegistryPath,
+ getSettingsPath,
+ getWorktreesDir,
+ expandHome,
+} from './cache-layout';
+
+export {
+ git,
+ isGitRepo,
+ gitRoot,
+ getDefaultBranch,
+ GitCommandError,
+ RepoNotFoundError,
+ InvalidGitHubUrlError,
+} from './git-runner';
+
+export {
+ parseGitHubInput,
+ computeRepoId,
+ importGitHubRepo,
+ type ParsedGitHubRepo,
+ type ImportGitHubOptions,
+} from './github-clone';
+
+export { importLocalRepo, type ImportLocalOptions } from './local-open';
+
+export {
+ listCommits,
+ getCommit,
+ getCommitDetail,
+ getChangedFiles,
+ getChangedFilesForRange,
+ listBranches,
+ resolveRef,
+ countChangedFiles,
+ type ListCommitsOptions,
+} from './commits';
+
+export {
+ createWorktree,
+ removeWorktree,
+ worktreePath,
+ type WorktreeOptions,
+} from './worktree';
diff --git a/packages/codedelta-repo-manager/src/local-open.ts b/packages/codedelta-repo-manager/src/local-open.ts
new file mode 100644
index 000000000..fda2fea13
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/local-open.ts
@@ -0,0 +1,48 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import type { RepoRef } from '@codedelta/types';
+import { expandHome } from './cache-layout';
+import { computeRepoId } from './github-clone';
+import { git, getDefaultBranch, gitRoot, isGitRepo, RepoNotFoundError } from './git-runner';
+
+export interface ImportLocalOptions {
+ /** Require path to exist and be a git repo. */
+ validate?: boolean;
+}
+
+/** Register a local git repository (no copy — uses the path directly). */
+export function importLocalRepo(inputPath: string, options: ImportLocalOptions = {}): RepoRef {
+ const expanded = expandHome(inputPath.trim());
+ const absPath = path.resolve(expanded);
+
+ if (options.validate !== false) {
+ if (!fs.existsSync(absPath)) {
+ throw new RepoNotFoundError(`Local path does not exist: ${absPath}`);
+ }
+ if (!isGitRepo(absPath)) {
+ throw new RepoNotFoundError(`Not a git repository: ${absPath}`);
+ }
+ }
+
+ const root = gitRoot(absPath);
+ const normalizedInput = root;
+ const repoId = computeRepoId(normalizedInput);
+ const defaultBranch = getDefaultBranch(root);
+
+ let remoteUrl: string | undefined;
+ try {
+ remoteUrl = git(['config', '--get', 'remote.origin.url'], { cwd: root });
+ } catch {
+ remoteUrl = undefined;
+ }
+
+ return {
+ id: repoId,
+ source: 'local',
+ input: inputPath.trim(),
+ clonePath: root,
+ defaultBranch,
+ remoteUrl: remoteUrl || undefined,
+ importedAt: new Date().toISOString(),
+ };
+}
diff --git a/packages/codedelta-repo-manager/src/worktree.ts b/packages/codedelta-repo-manager/src/worktree.ts
new file mode 100644
index 000000000..6079c8848
--- /dev/null
+++ b/packages/codedelta-repo-manager/src/worktree.ts
@@ -0,0 +1,47 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import { getWorktreesDir } from './cache-layout';
+import { git } from './git-runner';
+
+export interface WorktreeOptions {
+ cacheRoot: string;
+ repoId: string;
+}
+
+/**
+ * Create a detached worktree at a specific commit for snapshot indexing.
+ * Phase 2: used by snapshot-manager.
+ */
+export function createWorktree(
+ clonePath: string,
+ commitHash: string,
+ dest: string,
+): string {
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
+
+ if (fs.existsSync(dest)) {
+ try {
+ git(['worktree', 'remove', '--force', dest], { cwd: clonePath });
+ } catch {
+ fs.rmSync(dest, { recursive: true, force: true });
+ }
+ }
+
+ git(['worktree', 'add', '--detach', dest, commitHash], { cwd: clonePath, captureStderr: true });
+ return dest;
+}
+
+/** Default worktree path for a repo commit. Full hash avoids prefix collisions. */
+export function worktreePath(options: WorktreeOptions, commitHash: string): string {
+ return path.join(getWorktreesDir(options.cacheRoot, options.repoId), commitHash);
+}
+
+/** Remove a worktree directory. */
+export function removeWorktree(clonePath: string, worktreeDir: string): void {
+ if (!fs.existsSync(worktreeDir)) return;
+ try {
+ git(['worktree', 'remove', '--force', worktreeDir], { cwd: clonePath });
+ } catch {
+ fs.rmSync(worktreeDir, { recursive: true, force: true });
+ }
+}
diff --git a/packages/codedelta-repo-manager/tsconfig.json b/packages/codedelta-repo-manager/tsconfig.json
new file mode 100644
index 000000000..d8eed40e6
--- /dev/null
+++ b/packages/codedelta-repo-manager/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-server/__tests__/server.test.ts b/packages/codedelta-server/__tests__/server.test.ts
new file mode 100644
index 000000000..038465034
--- /dev/null
+++ b/packages/codedelta-server/__tests__/server.test.ts
@@ -0,0 +1,180 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import { execFileSync } from 'child_process';
+import request from 'supertest';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import { createApp } from '../src';
+
+function run(cmd: string, cwd: string): void {
+ execFileSync('sh', ['-c', cmd], { cwd, stdio: 'pipe' });
+}
+
+describe('codedelta-server (no git)', () => {
+ it('returns health check', async () => {
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ const cacheRoot = path.join(tmpDir, '.codedelta');
+ const { app } = createApp({ cacheRoot });
+ const res = await request(app).get('/api/health');
+ expect(res.status).toBe(200);
+ expect(res.body.product).toBe('CodeDelta');
+ expect(res.body).toHaveProperty('gitAvailable');
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('serves static UI with SPA fallback', async () => {
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ const cacheRoot = path.join(tmpDir, '.codedelta');
+ const staticRoot = path.join(tmpDir, 'web');
+ fs.mkdirSync(staticRoot, { recursive: true });
+ fs.writeFileSync(path.join(staticRoot, 'index.html'), 'ui');
+ const { app } = createApp({ cacheRoot, staticRoot });
+ const health = await request(app).get('/api/health');
+ expect(health.status).toBe(200);
+ const ui = await request(app).get('/');
+ expect(ui.status).toBe(200);
+ expect(ui.text).toContain('ui');
+ const spa = await request(app).get('/repos/foo/timeline');
+ expect(spa.status).toBe(200);
+ expect(spa.text).toContain('ui');
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('returns provider settings with none default', async () => {
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ const cacheRoot = path.join(tmpDir, '.codedelta');
+ const { app } = createApp({ cacheRoot });
+ const res = await request(app).get('/api/settings/provider');
+ expect(res.status).toBe(200);
+ expect(res.body.kind).toBe('none');
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('serves an API-only landing page on GET / when no UI is configured', async () => {
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ const cacheRoot = path.join(tmpDir, '.codedelta');
+ const { app } = createApp({ cacheRoot });
+ const res = await request(app).get('/');
+ expect(res.status).toBe(200);
+ expect(res.text).toContain('CodeDelta API is running');
+ expect(res.text).toContain('5173');
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('reports uiMode in health check', async () => {
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ const cacheRoot = path.join(tmpDir, '.codedelta');
+ const { app } = createApp({ cacheRoot, devUiUrl: 'http://localhost:5173' });
+ const res = await request(app).get('/api/health');
+ expect(res.status).toBe(200);
+ expect(res.body.uiMode).toBe('dev-proxy');
+ expect(res.body.servesUi).toBe(true);
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('returns codex auth status payload', async () => {
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ const cacheRoot = path.join(tmpDir, '.codedelta');
+ const { app } = createApp({ cacheRoot });
+ const res = await request(app).get('/api/settings/provider/codex-status');
+ expect(res.status).toBe(200);
+ expect(res.body).toHaveProperty('configured');
+ expect(res.body).toHaveProperty('codexHome');
+ expect(res.body).toHaveProperty('message');
+ expect(res.body).not.toHaveProperty('access_token');
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+});
+
+describe('codedelta-server (git)', () => {
+ let tmpDir: string;
+ let cacheRoot: string;
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-server-'));
+ cacheRoot = path.join(tmpDir, '.codedelta');
+ run('git init -b main', tmpDir);
+ run('git config user.email "test@example.com"', tmpDir);
+ run('git config user.name "Test User"', tmpDir);
+ fs.writeFileSync(path.join(tmpDir, 'README.md'), '# test\n');
+ fs.writeFileSync(path.join(tmpDir, 'index.ts'), 'export const base = 1;\n');
+ run('git add README.md index.ts && git commit -m "initial commit"', tmpDir);
+ });
+
+ afterEach(() => {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('imports local repo and lists commits', async () => {
+ const { app } = createApp({ cacheRoot });
+ const importRes = await request(app)
+ .post('/api/repos/import')
+ .send({ source: 'local', input: tmpDir });
+ expect(importRes.status).toBe(201);
+ expect(importRes.body.id).toBeTruthy();
+
+ const repoId = importRes.body.id as string;
+ const commitsRes = await request(app).get(`/api/repos/${repoId}/commits`);
+ expect(commitsRes.status).toBe(200);
+ expect(commitsRes.body.length).toBe(1);
+ expect(commitsRes.body[0].message).toBe('initial commit');
+ });
+
+ it('compares two commits', async () => {
+ fs.writeFileSync(path.join(tmpDir, 'auth.ts'), 'export function login() {}\n');
+ run('git add auth.ts && git commit -m "add auth"', tmpDir);
+
+ const hashes = execFileSync('git', ['log', '--format=%H', '-2'], {
+ cwd: tmpDir,
+ encoding: 'utf8',
+ })
+ .trim()
+ .split('\n');
+ const head = hashes[0]!;
+ const base = hashes[1]!;
+
+ const { app } = createApp({ cacheRoot });
+ const importRes = await request(app)
+ .post('/api/repos/import')
+ .send({ source: 'local', input: tmpDir });
+ const repoId = importRes.body.id as string;
+
+ const compareRes = await request(app).get(
+ `/api/repos/${repoId}/compare?base=${base}&head=${head}`,
+ );
+ expect(compareRes.status).toBe(200);
+ expect(compareRes.body.graphDiff).toBeDefined();
+ expect(compareRes.body.impact.score).toBeGreaterThanOrEqual(0);
+ expect(compareRes.body.base.type).toBe('commit');
+
+ const diffRes = await request(app).get(
+ `/api/repos/${repoId}/diff?base=${base}&head=${head}&file=${encodeURIComponent('auth.ts')}`,
+ );
+ expect(diffRes.status).toBe(200);
+ expect(diffRes.body.file).toBe('auth.ts');
+ expect(typeof diffRes.body.patch).toBe('string');
+ expect(Array.isArray(diffRes.body.hunks)).toBe(true);
+
+ });
+
+ it('traces commits in no-ai mode with evidence', async () => {
+ fs.writeFileSync(path.join(tmpDir, 'auth.ts'), 'export function login() {}\n');
+ run('git add auth.ts && git commit -m "add auth callback handler"', tmpDir);
+
+ const { app } = createApp({ cacheRoot });
+ const importRes = await request(app).post('/api/repos/import').send({ source: 'local', input: tmpDir });
+ const repoId = importRes.body.id as string;
+
+ const traceRes = await request(app).post(`/api/repos/${repoId}/trace`).send({
+ question: 'when did auth callback handler change?',
+ commitLimit: 20,
+ includeDiffEvidence: true,
+ });
+
+ expect(traceRes.status).toBe(200);
+ expect(traceRes.body.question).toContain('auth callback');
+ expect(Array.isArray(traceRes.body.candidates)).toBe(true);
+ expect(Array.isArray(traceRes.body.evidence)).toBe(true);
+ expect(traceRes.body.provider.used).toBe(false);
+ });
+});
diff --git a/packages/codedelta-server/__tests__/trace-provider-output.test.ts b/packages/codedelta-server/__tests__/trace-provider-output.test.ts
new file mode 100644
index 000000000..d90d41239
--- /dev/null
+++ b/packages/codedelta-server/__tests__/trace-provider-output.test.ts
@@ -0,0 +1,116 @@
+import { describe, expect, it } from 'vitest';
+import type { CommitInfo, TraceCandidateCommit, TraceEvidenceItem } from '@codedelta/types';
+import {
+ buildTraceProviderSystemPrompt,
+ extractJsonFromModelText,
+ validateProviderTraceOutput,
+} from '../src/services/trace-provider-output';
+
+function commit(hash: string): CommitInfo {
+ return {
+ hash,
+ shortHash: hash.slice(0, 7),
+ message: 'fix auth',
+ author: 'a',
+ authorEmail: 'a@x.com',
+ date: new Date().toISOString(),
+ parents: ['parent1'],
+ changedFilesCount: 1,
+ };
+}
+
+const candidates: TraceCandidateCommit[] = [
+ {
+ commit: commit('abc1234567890123456789012345678901234567890'),
+ relevanceScore: 50,
+ reasons: ['message match'],
+ matchedTerms: ['auth'],
+ changedFiles: [{ path: 'src/auth.ts', status: 'modified' }],
+ previousCommitHash: 'parent1',
+ },
+];
+
+const evidence: TraceEvidenceItem[] = [
+ {
+ id: 'ev-abc1234567890123456789012345678901234567890-message',
+ kind: 'commit-message',
+ commitHash: candidates[0]!.commit.hash,
+ title: 'msg',
+ detail: 'fix auth',
+ },
+];
+
+describe('trace-provider-output', () => {
+ it('extracts JSON from fenced model output', () => {
+ const raw = '```json\n{"directAnswer":"x","confidence":"low"}\n```';
+ const parsed = extractJsonFromModelText(raw);
+ expect(parsed).toEqual({ directAnswer: 'x', confidence: 'low' });
+ });
+
+ it('accepts valid provider output with evidence refs', () => {
+ const hash = candidates[0]!.commit.hash;
+ const evId = evidence[0]!.id;
+ const result = validateProviderTraceOutput(
+ {
+ directAnswer: 'Likely introduced in candidate commit.',
+ directAnswerEvidenceRefs: [evId],
+ mostLikelyCommitHash: hash,
+ confidence: 'medium',
+ evolution: [
+ {
+ label: 'candidate',
+ commitHash: hash,
+ summary: 'Auth path changed.',
+ evidenceRefs: [evId],
+ },
+ ],
+ uncertainty: [],
+ suggestedNextChecks: ['Open Delta View'],
+ },
+ {
+ candidates,
+ evidence,
+ allCommitHashes: [hash, 'parent1'],
+ },
+ );
+ expect(result.ok).toBe(true);
+ if (result.ok) {
+ expect(result.value.directAnswerEvidenceRefs).toEqual([evId]);
+ expect(result.value.mostLikelyCommitHash).toBe(hash);
+ }
+ });
+
+ it('rejects unknown commit hash', () => {
+ const result = validateProviderTraceOutput(
+ {
+ directAnswer: 'bad',
+ mostLikelyCommitHash: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
+ },
+ { candidates, evidence, allCommitHashes: [candidates[0]!.commit.hash] },
+ );
+ expect(result.ok).toBe(false);
+ });
+
+ it('drops unknown evidence refs', () => {
+ const hash = candidates[0]!.commit.hash;
+ const result = validateProviderTraceOutput(
+ {
+ directAnswer: 'partial',
+ directAnswerEvidenceRefs: ['ev-fake', evidence[0]!.id],
+ mostLikelyCommitHash: hash,
+ confidence: 'high',
+ },
+ { candidates, evidence, allCommitHashes: [hash] },
+ );
+ expect(result.ok).toBe(true);
+ if (result.ok) {
+ expect(result.value.directAnswerEvidenceRefs).toEqual([evidence[0]!.id]);
+ }
+ });
+
+ it('system prompt requires evidence-grounded JSON', () => {
+ const prompt = buildTraceProviderSystemPrompt();
+ expect(prompt).toContain('Never invent');
+ expect(prompt).toContain('directAnswerEvidenceRefs');
+ });
+});
diff --git a/packages/codedelta-server/package.json b/packages/codedelta-server/package.json
new file mode 100644
index 000000000..4760210ee
--- /dev/null
+++ b/packages/codedelta-server/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@codedelta/server",
+ "version": "0.1.0",
+ "description": "HTTP API server for CodeDelta",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsx watch src/index.ts",
+ "start": "node dist/index.js",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/delta-summary": "*",
+ "@codedelta/graph-diff": "*",
+ "@codedelta/graph-subgraph": "*",
+ "@codedelta/impact-score": "*",
+ "@codedelta/provider-runtime": "*",
+ "@codedelta/repo-manager": "*",
+ "@codedelta/snapshot-manager": "*",
+ "@codedelta/trace-engine": "*",
+ "@codedelta/types": "*",
+ "@codedelta/wiki-engine": "*",
+ "cors": "^2.8.5",
+ "express": "^4.21.2",
+ "http-proxy-middleware": "^3.0.6"
+ },
+ "devDependencies": {
+ "@types/cors": "^2.8.17",
+ "@types/express": "^5.0.0",
+ "@types/supertest": "^6.0.2",
+ "supertest": "^7.0.0",
+ "tsx": "^4.19.2"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-server/src/index.ts b/packages/codedelta-server/src/index.ts
new file mode 100644
index 000000000..2fa1a03a8
--- /dev/null
+++ b/packages/codedelta-server/src/index.ts
@@ -0,0 +1,162 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import { execFileSync } from 'child_process';
+import express from 'express';
+import cors from 'cors';
+import { createProxyMiddleware } from 'http-proxy-middleware';
+import { JobStore } from './jobs';
+import { createReposRouter } from './routes/repos';
+import { createSettingsRouter } from './routes/settings';
+import { createWikiRouter } from './routes/wiki';
+import { RepoRegistry, SettingsStore } from './store/repo-registry';
+
+export interface CreateAppOptions {
+ cacheRoot?: string;
+ /** Serve built web UI from this directory (desktop / single-port production). */
+ staticRoot?: string;
+ /** Dev-only: proxy non-API routes to the Vite dev server (e.g. http://localhost:5173). */
+ devUiUrl?: string;
+}
+
+function apiOnlyLandingHtml(viteUrl: string): string {
+ return `
+
+
+
+ CodeDelta API
+
+
+
+ CodeDelta API is running
+ This port serves /api/* only. The React UI is not bundled here in dev mode.
+ Start the full dev stack from the repo root:
+ npm run dev:codedelta
+ Then open ${viteUrl} (Vite) or enable UI proxy via CODEDELTA_DEV_UI_URL to use this port for the UI too.
+
+`;
+}
+
+function isGitAvailable(): boolean {
+ try {
+ execFileSync('git', ['--version'], { stdio: 'ignore' });
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+/** Resolve cache root from env or desktop defaults. */
+export function resolveCacheRoot(): string | undefined {
+ const env = process.env.CODEDELTA_CACHE_DIR;
+ if (env) {
+ return path.isAbsolute(env) ? env : path.resolve(process.cwd(), env);
+ }
+ if (process.env.CODEDELTA_DESKTOP === '1') {
+ const base =
+ process.platform === 'darwin'
+ ? path.join(os.homedir(), 'Library', 'Application Support', 'CodeDelta')
+ : process.env.APPDATA
+ ? path.join(process.env.APPDATA, 'CodeDelta')
+ : path.join(os.homedir(), '.codedelta');
+ return base;
+ }
+ return undefined;
+}
+
+export function createApp(options: CreateAppOptions = {}) {
+ const registry = new RepoRegistry(options.cacheRoot);
+ const settings = new SettingsStore(options.cacheRoot);
+ const jobs = new JobStore();
+
+ const app = express();
+ app.use(cors());
+ app.use(express.json());
+
+ const gitAvailable = isGitAvailable();
+
+ const uiMode = options.staticRoot ? 'static' : options.devUiUrl ? 'dev-proxy' : 'api-only';
+
+ app.get('/api/health', (_req, res) => {
+ res.json({
+ status: 'ok',
+ product: 'CodeDelta',
+ gitAvailable,
+ servesUi: uiMode !== 'api-only',
+ uiMode,
+ devUiUrl: options.devUiUrl,
+ });
+ });
+
+ app.use('/api/repos/:id/wiki', createWikiRouter(registry, settings, jobs));
+ app.use('/api/repos', createReposRouter(registry, settings));
+ app.use('/api/settings', createSettingsRouter(settings));
+
+ if (options.staticRoot) {
+ const staticRoot = path.resolve(options.staticRoot);
+ if (!fs.existsSync(path.join(staticRoot, 'index.html'))) {
+ console.warn(`CODEDELTA_STATIC_DIR: index.html not found in ${staticRoot}`);
+ }
+ app.use(express.static(staticRoot));
+ app.get('*', (req, res, next) => {
+ if (req.path.startsWith('/api')) {
+ next();
+ return;
+ }
+ res.sendFile(path.join(staticRoot, 'index.html'), (err) => {
+ if (err) next(err);
+ });
+ });
+ } else if (options.devUiUrl) {
+ app.use(
+ createProxyMiddleware({
+ target: options.devUiUrl,
+ changeOrigin: true,
+ ws: true,
+ }),
+ );
+ } else {
+ app.get('/', (_req, res) => {
+ res.type('html').send(apiOnlyLandingHtml('http://localhost:5173'));
+ });
+ }
+
+ return { app, registry, settings };
+}
+
+export function startServer(port = 3847, options: CreateAppOptions = {}) {
+ const { app } = createApp(options);
+ return app.listen(port, () => {
+ const url = `http://localhost:${port}`;
+ if (options.staticRoot) {
+ console.log(`CodeDelta API + UI listening on ${url}`);
+ return;
+ }
+ if (options.devUiUrl) {
+ console.log(`CodeDelta dev listening on ${url} (UI proxied from ${options.devUiUrl})`);
+ console.log(`Open ${url} in your browser.`);
+ return;
+ }
+ console.log(`CodeDelta API listening on ${url}`);
+ console.log('Web UI: run Vite separately and open http://localhost:5173');
+ });
+}
+
+export function resolveServerOptions(): CreateAppOptions {
+ const staticDir = process.env.CODEDELTA_STATIC_DIR;
+ const devUiUrl = process.env.CODEDELTA_DEV_UI_URL?.trim();
+ return {
+ cacheRoot: resolveCacheRoot(),
+ staticRoot: staticDir ? path.resolve(staticDir) : undefined,
+ devUiUrl: devUiUrl || undefined,
+ };
+}
+
+if (require.main === module) {
+ const port = parseInt(process.env.CODEDELTA_PORT ?? '3847', 10);
+ startServer(port, resolveServerOptions());
+}
diff --git a/packages/codedelta-server/src/jobs/index.ts b/packages/codedelta-server/src/jobs/index.ts
new file mode 100644
index 000000000..f82de42a0
--- /dev/null
+++ b/packages/codedelta-server/src/jobs/index.ts
@@ -0,0 +1,103 @@
+import { randomUUID } from 'crypto';
+
+export type JobState = 'queued' | 'running' | 'done' | 'error';
+
+export interface JobProgress {
+ total?: number;
+ completed?: number;
+ phase?: string;
+}
+
+export interface Job {
+ id: string;
+ kind: string;
+ /** Dedupe key — one active job per key. */
+ key: string;
+ state: JobState;
+ createdAt: number;
+ updatedAt: number;
+ progress: JobProgress;
+ error?: string;
+}
+
+const FINISHED_JOB_TTL_MS = 30 * 60 * 1000;
+
+/**
+ * Generic in-memory async job store (wiki generation today; reusable for
+ * trace/compare task-ification later). Single-process by design — matches the
+ * local-first server/desktop deployment.
+ */
+export class JobStore {
+ private jobs = new Map();
+
+ get(id: string): Job | undefined {
+ this.evictFinished();
+ return this.jobs.get(id);
+ }
+
+ /** Active (queued/running) job for a dedupe key, if any. */
+ getActiveByKey(key: string): Job | undefined {
+ this.evictFinished();
+ for (const job of this.jobs.values()) {
+ if (job.key === key && (job.state === 'queued' || job.state === 'running')) {
+ return job;
+ }
+ }
+ return undefined;
+ }
+
+ /**
+ * Start a job unless one is already active for the key. The runner receives
+ * a progress callback; completion/failure is recorded automatically.
+ */
+ start(
+ kind: string,
+ key: string,
+ runner: (reportProgress: (progress: JobProgress) => void) => Promise,
+ ): Job {
+ const existing = this.getActiveByKey(key);
+ if (existing) return existing;
+
+ const job: Job = {
+ id: randomUUID(),
+ kind,
+ key,
+ state: 'queued',
+ createdAt: Date.now(),
+ updatedAt: Date.now(),
+ progress: {},
+ };
+ this.jobs.set(job.id, job);
+
+ const reportProgress = (progress: JobProgress): void => {
+ job.progress = { ...job.progress, ...progress };
+ job.updatedAt = Date.now();
+ };
+
+ job.state = 'running';
+ runner(reportProgress)
+ .then(() => {
+ job.state = 'done';
+ job.updatedAt = Date.now();
+ })
+ .catch((err) => {
+ job.state = 'error';
+ job.error = err instanceof Error ? err.message : String(err);
+ job.updatedAt = Date.now();
+ });
+
+ return job;
+ }
+
+ private evictFinished(): void {
+ const now = Date.now();
+ for (const [id, job] of this.jobs) {
+ if (
+ (job.state === 'done' || job.state === 'error') &&
+ now - job.updatedAt > FINISHED_JOB_TTL_MS
+ ) {
+ this.jobs.delete(id);
+ }
+ }
+ }
+}
diff --git a/packages/codedelta-server/src/routes/params.ts b/packages/codedelta-server/src/routes/params.ts
new file mode 100644
index 000000000..997011982
--- /dev/null
+++ b/packages/codedelta-server/src/routes/params.ts
@@ -0,0 +1,5 @@
+/** Route param helper — Express params may be string | string[]. */
+export function param(value: string | string[] | undefined): string {
+ if (Array.isArray(value)) return value[0] ?? '';
+ return value ?? '';
+}
diff --git a/packages/codedelta-server/src/routes/repos.ts b/packages/codedelta-server/src/routes/repos.ts
new file mode 100644
index 000000000..a4ec70bb2
--- /dev/null
+++ b/packages/codedelta-server/src/routes/repos.ts
@@ -0,0 +1,264 @@
+import { Router, type Request, type Response } from 'express';
+import {
+ getCommitDetail,
+ importGitHubRepo,
+ importLocalRepo,
+ InvalidGitHubUrlError,
+ listBranches,
+ listCommits,
+ RepoNotFoundError,
+} from '@codedelta/repo-manager';
+import type { ImportRepoRequest, PanoramaEnrichRequest, TraceQuestion } from '@codedelta/types';
+import { compareCommits, CompareError } from '../services/compare';
+import { getFileDiff } from '../services/diff';
+import { enrichPanoramaNodes, getPanorama, PanoramaError } from '../services/panorama';
+import { runTrace, TraceError } from '../services/trace';
+import { RepoRegistry, SettingsStore } from '../store/repo-registry';
+import { param } from './params';
+
+export function createReposRouter(registry: RepoRegistry, settings: SettingsStore): Router {
+ const router = Router();
+
+ router.post('/import', (req: Request, res: Response) => {
+ const body = req.body as ImportRepoRequest;
+ if (!body?.source || !body?.input?.trim()) {
+ res.status(400).json({ error: 'Missing source or input' });
+ return;
+ }
+
+ try {
+ const cacheRoot = registry.getCacheRoot();
+ let ref;
+ if (body.source === 'github') {
+ ref = importGitHubRepo(body.input, { cacheRoot });
+ } else if (body.source === 'local') {
+ ref = importLocalRepo(body.input);
+ } else {
+ res.status(400).json({ error: 'source must be github or local' });
+ return;
+ }
+ registry.add(ref);
+ res.status(201).json(ref);
+ } catch (err) {
+ if (err instanceof InvalidGitHubUrlError) {
+ res.status(400).json({ error: err.message });
+ return;
+ }
+ if (err instanceof RepoNotFoundError) {
+ res.status(404).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Import failed' });
+ }
+ });
+
+ router.get('/', (_req: Request, res: Response) => {
+ res.json(registry.list());
+ });
+
+ router.get('/:id', (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const ref = registry.get(id);
+ if (!ref) {
+ res.status(404).json({ error: 'Repository not found' });
+ return;
+ }
+ res.json(ref);
+ });
+
+ router.get('/:id/branches', (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const ref = registry.get(id);
+ if (!ref) {
+ res.status(404).json({ error: 'Repository not found' });
+ return;
+ }
+ try {
+ res.json(listBranches(ref.clonePath));
+ } catch (err) {
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Failed to list branches' });
+ }
+ });
+
+ router.get('/:id/panorama', async (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const commit = (req.query.commit as string | undefined)?.trim();
+ const base = (req.query.base as string | undefined)?.trim();
+ const head = (req.query.head as string | undefined)?.trim();
+ const root = (req.query.root as string | undefined)?.trim();
+ const depth = parseInt(String(req.query.depth ?? '3'), 10);
+ const maxNodes = parseInt(String(req.query.maxNodes ?? '200'), 10);
+ const highlight = (req.query.highlight as string | undefined)?.trim();
+ const traceSymbols = (req.query.traceSymbols as string | undefined)
+ ?.split(',')
+ .map((s) => s.trim())
+ .filter(Boolean);
+ const traceEntryPoints = (req.query.traceEntryPoints as string | undefined)
+ ?.split(',')
+ .map((s) => s.trim())
+ .filter(Boolean);
+
+ try {
+ const result = await getPanorama(registry, id, {
+ commit,
+ base,
+ head,
+ root,
+ depth: Number.isFinite(depth) ? depth : 3,
+ maxNodes: Number.isFinite(maxNodes) ? maxNodes : 200,
+ highlight: highlight === 'trace' ? 'trace' : undefined,
+ traceSymbols,
+ traceEntryPoints,
+ });
+ res.json(result);
+ } catch (err) {
+ if (err instanceof PanoramaError || err instanceof CompareError) {
+ res.status(err.statusCode).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({
+ error: err instanceof Error ? err.message : 'Panorama failed',
+ });
+ }
+ });
+
+ router.post('/:id/panorama/enrich', async (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const body = (req.body ?? {}) as Partial;
+ try {
+ const result = await enrichPanoramaNodes(registry, settings, id, {
+ commit: body.commit ?? '',
+ nodeIds: body.nodeIds ?? [],
+ });
+ res.json(result);
+ } catch (err) {
+ if (err instanceof PanoramaError) {
+ res.status(err.statusCode).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({
+ error: err instanceof Error ? err.message : 'Enrich failed',
+ });
+ }
+ });
+
+ router.get('/:id/compare', async (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const base = (req.query.base as string | undefined)?.trim();
+ const head = (req.query.head as string | undefined)?.trim();
+
+ if (!base || !head) {
+ res.status(400).json({ error: 'Query parameters base and head are required' });
+ return;
+ }
+
+ try {
+ const result = await compareCommits(registry, id, base, head);
+ res.json(result);
+ } catch (err) {
+ if (err instanceof CompareError) {
+ res.status(err.statusCode).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({
+ error: err instanceof Error ? err.message : 'Compare failed',
+ });
+ }
+ });
+
+
+ router.get('/:id/diff', async (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const base = (req.query.base as string | undefined)?.trim();
+ const head = (req.query.head as string | undefined)?.trim();
+ const file = (req.query.file as string | undefined)?.trim();
+
+ if (!base || !head || !file) {
+ res.status(400).json({ error: 'Query parameters base, head and file are required' });
+ return;
+ }
+
+ try {
+ const result = await getFileDiff(registry, id, base, head, file);
+ res.json(result);
+ } catch (err) {
+ if (err instanceof CompareError) {
+ res.status(err.statusCode).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Diff failed' });
+ }
+ });
+
+ router.get('/:id/commits', (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const ref = registry.get(id);
+ if (!ref) {
+ res.status(404).json({ error: 'Repository not found' });
+ return;
+ }
+
+ const branch = (req.query.branch as string | undefined) ?? ref.defaultBranch;
+ const limit = parseInt(String(req.query.limit ?? '50'), 10);
+ const skip = parseInt(String(req.query.skip ?? '0'), 10);
+
+ try {
+ res.json(listCommits(ref.clonePath, branch, { limit, skip }));
+ } catch (err) {
+ res.status(500).json({ error: err instanceof Error ? err.message : 'Failed to list commits' });
+ }
+ });
+
+ router.get('/:id/commits/:hash', (req: Request, res: Response) => {
+ const id = param(req.params.id);
+ const hash = param(req.params.hash);
+ const ref = registry.get(id);
+ if (!ref) {
+ res.status(404).json({ error: 'Repository not found' });
+ return;
+ }
+
+ try {
+ res.json(getCommitDetail(ref.clonePath, hash));
+ } catch (err) {
+ res.status(404).json({ error: err instanceof Error ? err.message : 'Commit not found' });
+ }
+ });
+
+ router.post('/:id/delta', (_req: Request, res: Response) => {
+ res.status(410).json({
+ error: 'Use GET /api/repos/:id/compare?base=&head= instead',
+ message: 'POST /delta was replaced by GET /compare in Phase 2',
+ });
+ });
+
+ router.post('/:id/trace', async (req: Request, res: Response) => {
+ const repoId = param(req.params.id);
+ const body = (req.body ?? {}) as Partial;
+ const question = (body.question ?? '').trim();
+ if (!question) {
+ res.status(400).json({ error: 'question is required' });
+ return;
+ }
+ try {
+ const result = await runTrace(registry, settings, {
+ repoId,
+ question,
+ branch: body.branch,
+ commitLimit: body.commitLimit,
+ includeDiffEvidence: body.includeDiffEvidence,
+ });
+ res.json(result);
+ } catch (err) {
+ if (err instanceof TraceError || err instanceof CompareError) {
+ res.status(err.statusCode).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({
+ error: err instanceof Error ? err.message : 'Trace failed',
+ });
+ }
+ });
+
+ return router;
+}
diff --git a/packages/codedelta-server/src/routes/settings.ts b/packages/codedelta-server/src/routes/settings.ts
new file mode 100644
index 000000000..626b8b754
--- /dev/null
+++ b/packages/codedelta-server/src/routes/settings.ts
@@ -0,0 +1,32 @@
+import { readCodexAuthStatus } from '@codedelta/provider-runtime';
+import { Router, type Request, type Response } from 'express';
+import type { ModelProviderConfig } from '@codedelta/types';
+import { SettingsStore } from '../store/repo-registry';
+
+export function createSettingsRouter(settings: SettingsStore): Router {
+ const router = Router();
+
+ router.get('/provider', (_req: Request, res: Response) => {
+ res.json(settings.getProvider());
+ });
+
+ router.get('/provider/codex-status', (_req: Request, res: Response) => {
+ res.json(readCodexAuthStatus());
+ });
+
+ router.put('/provider', (req: Request, res: Response) => {
+ const body = req.body as ModelProviderConfig;
+ if (!body?.kind) {
+ res.status(400).json({ error: 'Missing provider kind' });
+ return;
+ }
+ const allowed = ['codex-oauth', 'openai', 'openai-compatible', 'anthropic', 'ollama', 'none'];
+ if (!allowed.includes(body.kind)) {
+ res.status(400).json({ error: `Invalid provider kind: ${body.kind}` });
+ return;
+ }
+ res.json(settings.setProvider(body));
+ });
+
+ return router;
+}
diff --git a/packages/codedelta-server/src/routes/wiki.ts b/packages/codedelta-server/src/routes/wiki.ts
new file mode 100644
index 000000000..27257b5eb
--- /dev/null
+++ b/packages/codedelta-server/src/routes/wiki.ts
@@ -0,0 +1,126 @@
+import { Router, type Request, type Response } from 'express';
+import type { WikiAskRequest } from '@codedelta/types';
+import type { JobStore } from '../jobs';
+import {
+ askWiki,
+ getWikiAsset,
+ getWikiPage,
+ getWikiStatus,
+ getWikiToc,
+ startWikiGeneration,
+ WikiError,
+} from '../services/wiki';
+import { RepoRegistry, SettingsStore } from '../store/repo-registry';
+import { param } from './params';
+
+function handleError(res: Response, err: unknown, fallback: string): void {
+ if (err instanceof WikiError) {
+ res.status(err.statusCode).json({ error: err.message });
+ return;
+ }
+ res.status(500).json({ error: err instanceof Error ? err.message : fallback });
+}
+
+/** Mounted at /api/repos/:id/wiki (mergeParams gives access to :id). */
+export function createWikiRouter(
+ registry: RepoRegistry,
+ settings: SettingsStore,
+ jobs: JobStore,
+): Router {
+ const router = Router({ mergeParams: true });
+
+ router.post('/generate', (req: Request, res: Response) => {
+ const repoId = param((req.params as Record).id);
+ const commit = (req.query.commit as string | undefined)?.trim();
+ if (!commit) {
+ res.status(400).json({ error: 'Query parameter commit is required' });
+ return;
+ }
+ try {
+ const result = startWikiGeneration(registry, settings, jobs, repoId, commit);
+ if (result.alreadyReady) {
+ res.json({ status: 'ready' });
+ return;
+ }
+ res.status(202).json({ status: 'generating', jobId: result.jobId });
+ } catch (err) {
+ handleError(res, err, 'Wiki generation failed to start');
+ }
+ });
+
+ router.get('/status', (req: Request, res: Response) => {
+ const repoId = param((req.params as Record).id);
+ const commit = (req.query.commit as string | undefined)?.trim();
+ if (!commit) {
+ res.status(400).json({ error: 'Query parameter commit is required' });
+ return;
+ }
+ try {
+ res.json(getWikiStatus(registry, jobs, repoId, commit));
+ } catch (err) {
+ handleError(res, err, 'Wiki status failed');
+ }
+ });
+
+ router.get('/toc', (req: Request, res: Response) => {
+ const repoId = param((req.params as Record).id);
+ const commit = (req.query.commit as string | undefined)?.trim();
+ if (!commit) {
+ res.status(400).json({ error: 'Query parameter commit is required' });
+ return;
+ }
+ try {
+ res.json(getWikiToc(registry, repoId, commit));
+ } catch (err) {
+ handleError(res, err, 'Wiki TOC failed');
+ }
+ });
+
+ router.get('/page', (req: Request, res: Response) => {
+ const repoId = param((req.params as Record).id);
+ const commit = (req.query.commit as string | undefined)?.trim();
+ const section = (req.query.section as string | undefined)?.trim();
+ if (!commit || !section) {
+ res.status(400).json({ error: 'Query parameters commit and section are required' });
+ return;
+ }
+ try {
+ res.json(getWikiPage(registry, repoId, commit, section));
+ } catch (err) {
+ handleError(res, err, 'Wiki page failed');
+ }
+ });
+
+ router.get('/asset', (req: Request, res: Response) => {
+ const repoId = param((req.params as Record).id);
+ const commit = (req.query.commit as string | undefined)?.trim();
+ const filePath = (req.query.path as string | undefined)?.trim();
+ if (!commit || !filePath) {
+ res.status(400).json({ error: 'Query parameters commit and path are required' });
+ return;
+ }
+ try {
+ const asset = getWikiAsset(registry, repoId, commit, filePath);
+ res.type(asset.contentType).send(asset.body);
+ } catch (err) {
+ handleError(res, err, 'Wiki asset failed');
+ }
+ });
+
+ router.post('/ask', async (req: Request, res: Response) => {
+ const repoId = param((req.params as Record).id);
+ const body = (req.body ?? {}) as Partial;
+ try {
+ const result = await askWiki(registry, settings, repoId, {
+ commit: body.commit ?? '',
+ question: body.question ?? '',
+ history: body.history,
+ });
+ res.json(result);
+ } catch (err) {
+ handleError(res, err, 'Wiki ask failed');
+ }
+ });
+
+ return router;
+}
diff --git a/packages/codedelta-server/src/services/compare.ts b/packages/codedelta-server/src/services/compare.ts
new file mode 100644
index 000000000..36815833b
--- /dev/null
+++ b/packages/codedelta-server/src/services/compare.ts
@@ -0,0 +1,116 @@
+import { buildDeltaSummary } from '@codedelta/delta-summary';
+import { computeGraphDiff } from '@codedelta/graph-diff';
+import { computeImpactScore } from '@codedelta/impact-score';
+import { git, getChangedFilesForRange } from '@codedelta/repo-manager';
+import {
+ getOrBuildSnapshot,
+ readAnalyzerVersion,
+ resolveMonorepoRoot,
+ SnapshotBuildError,
+ SnapshotEmptyError,
+ SnapshotTimeoutError,
+ SnapshotTooLargeError,
+} from '@codedelta/snapshot-manager';
+import type { CompareResponse } from '@codedelta/types';
+import { RepoRegistry } from '../store/repo-registry';
+
+export class CompareError extends Error {
+ constructor(
+ message: string,
+ public readonly statusCode: number,
+ ) {
+ super(message);
+ this.name = 'CompareError';
+ }
+}
+
+function verifyCommit(clonePath: string, hash: string): void {
+ try {
+ git(['rev-parse', '--verify', hash], { cwd: clonePath });
+ } catch {
+ throw new CompareError(`Commit not found: ${hash}`, 404);
+ }
+}
+
+export async function compareCommits(
+ registry: RepoRegistry,
+ repoId: string,
+ baseHash: string,
+ headHash: string,
+): Promise {
+ const ref = registry.get(repoId);
+ if (!ref) {
+ throw new CompareError('Repository not found', 404);
+ }
+
+ verifyCommit(ref.clonePath, baseHash);
+ verifyCommit(ref.clonePath, headHash);
+
+ const cacheRoot = registry.getCacheRoot();
+ const analyzerVersion = readAnalyzerVersion(resolveMonorepoRoot());
+
+ const snapshotOpts = {
+ repoId,
+ clonePath: ref.clonePath,
+ cacheRoot,
+ analyzerVersion,
+ };
+
+ let baseSnap;
+ let headSnap;
+ try {
+ [baseSnap, headSnap] = await Promise.all([
+ getOrBuildSnapshot({ ...snapshotOpts, commitHash: baseHash }),
+ getOrBuildSnapshot({ ...snapshotOpts, commitHash: headHash }),
+ ]);
+ } catch (err: unknown) {
+ if (err instanceof SnapshotTimeoutError) {
+ throw new CompareError(err.message, 504);
+ }
+ if (err instanceof SnapshotTooLargeError) {
+ throw new CompareError(err.message, 413);
+ }
+ if (err instanceof SnapshotEmptyError) {
+ throw new CompareError(
+ 'Repository produced an empty structural graph. CodeGraph indexing did not run or found no symbols, and the TS/JS fallback found no .ts/.js files. Retry once; on desktop ensure the bundled Node is 22.5+ (rebuild with npm run build:desktop). Python and other languages need CodeGraph, not fallback.',
+ 422,
+ );
+ }
+ if (err instanceof SnapshotBuildError) {
+ throw new CompareError(`Snapshot build failed: ${err.message}`, 500);
+ }
+ throw new CompareError(
+ `Snapshot build failed: ${err instanceof Error ? err.message : String(err)}`,
+ 500,
+ );
+ }
+
+ const changedFiles = getChangedFilesForRange(ref.clonePath, baseHash, headHash);
+ const graphDiff = computeGraphDiff({
+ base: baseSnap,
+ head: headSnap,
+ changedFiles,
+ });
+
+ const impact = computeImpactScore(headHash, graphDiff, headSnap);
+ const deltaSummary = buildDeltaSummary(graphDiff, impact);
+
+ return {
+ repoId,
+ base: { type: 'commit', commitHash: baseHash, label: baseHash.slice(0, 7) },
+ head: { type: 'commit', commitHash: headHash, label: headHash.slice(0, 7) },
+ graphDiff,
+ impact,
+ deltaSummary,
+ baseMeta: {
+ nodeCount: baseSnap.nodeCount,
+ edgeCount: baseSnap.edgeCount,
+ extractionMethod: baseSnap.metadata?.extractionMethod ?? 'fallback',
+ },
+ headMeta: {
+ nodeCount: headSnap.nodeCount,
+ edgeCount: headSnap.edgeCount,
+ extractionMethod: headSnap.metadata?.extractionMethod ?? 'fallback',
+ },
+ };
+}
diff --git a/packages/codedelta-server/src/services/diff.ts b/packages/codedelta-server/src/services/diff.ts
new file mode 100644
index 000000000..363a5a9d6
--- /dev/null
+++ b/packages/codedelta-server/src/services/diff.ts
@@ -0,0 +1,97 @@
+import * as path from 'path';
+import { getChangedFilesForRange, git } from '@codedelta/repo-manager';
+import type { FileDiffHunk, FileDiffResponse } from '@codedelta/types';
+import { RepoRegistry } from '../store/repo-registry';
+import { CompareError } from './compare';
+
+function verifyCommit(clonePath: string, hash: string): void {
+ try {
+ git(['rev-parse', '--verify', hash], { cwd: clonePath });
+ } catch {
+ throw new CompareError(`Commit not found: ${hash}`, 404);
+ }
+}
+
+function normalizeRelativeFile(input: string): string {
+ const normalized = path.posix.normalize(input.replace(/\\/g, '/'));
+ if (normalized.startsWith('../') || normalized.includes('/../') || normalized === '..') {
+ throw new CompareError('Invalid file path', 400);
+ }
+ if (path.isAbsolute(normalized)) {
+ throw new CompareError('File must be a repository-relative path', 400);
+ }
+ return normalized;
+}
+
+function parseHunks(patch: string): FileDiffHunk[] {
+ const hunks: FileDiffHunk[] = [];
+ const lines = patch.split('\n');
+ let current: FileDiffHunk | null = null;
+
+ for (const line of lines) {
+ const m = line.match(/^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@(.*)$/);
+ if (m) {
+ if (current) hunks.push(current);
+ current = {
+ oldStart: Number(m[1]),
+ oldLines: Number(m[2] ?? '1'),
+ newStart: Number(m[3]),
+ newLines: Number(m[4] ?? '1'),
+ header: line,
+ lines: [],
+ };
+ continue;
+ }
+ if (current) current.lines.push(line);
+ }
+ if (current) hunks.push(current);
+ return hunks;
+}
+
+export async function getFileDiff(
+ registry: RepoRegistry,
+ repoId: string,
+ baseHash: string,
+ headHash: string,
+ fileInput: string,
+): Promise {
+ const ref = registry.get(repoId);
+ if (!ref) {
+ throw new CompareError('Repository not found', 404);
+ }
+
+ verifyCommit(ref.clonePath, baseHash);
+ verifyCommit(ref.clonePath, headHash);
+
+ const file = normalizeRelativeFile(fileInput);
+
+ const changedFiles = getChangedFilesForRange(ref.clonePath, baseHash, headHash);
+ const changed = changedFiles.find((f) => f.path === file || f.oldPath === file);
+ if (!changed) {
+ throw new CompareError('File not changed in selected range', 404);
+ }
+
+ let patch = '';
+ try {
+ patch = git(['diff', `${baseHash}..${headHash}`, '--', file], {
+ cwd: ref.clonePath,
+ captureStderr: true,
+ });
+ } catch {
+ throw new CompareError('Diff unavailable for selected file', 422);
+ }
+
+ if (!patch.trim()) {
+ throw new CompareError('Diff unavailable for selected file', 422);
+ }
+
+ return {
+ repoId,
+ base: baseHash,
+ head: headHash,
+ file: changed.path,
+ status: changed.status,
+ patch,
+ hunks: parseHunks(patch),
+ };
+}
diff --git a/packages/codedelta-server/src/services/panorama.ts b/packages/codedelta-server/src/services/panorama.ts
new file mode 100644
index 000000000..4a9d9d24f
--- /dev/null
+++ b/packages/codedelta-server/src/services/panorama.ts
@@ -0,0 +1,219 @@
+import {
+ applyTraceHighlight,
+ buildDeltaPanoramaGraph,
+ buildPanoramaGraph,
+ resolveNodeQuery,
+ resolvePanoramaBudget,
+} from '@codedelta/graph-subgraph';
+import { createProvider } from '@codedelta/provider-runtime';
+import { git } from '@codedelta/repo-manager';
+import {
+ getOrBuildSnapshot,
+ readAnalyzerVersion,
+ resolveMonorepoRoot,
+ SnapshotBuildError,
+ SnapshotEmptyError,
+ SnapshotTimeoutError,
+} from '@codedelta/snapshot-manager';
+import type {
+ CodeNode,
+ PanoramaEnrichRequest,
+ PanoramaEnrichResult,
+ PanoramaGraph,
+} from '@codedelta/types';
+import { RepoRegistry, SettingsStore } from '../store/repo-registry';
+
+export class PanoramaError extends Error {
+ constructor(
+ message: string,
+ public readonly statusCode: number,
+ ) {
+ super(message);
+ this.name = 'PanoramaError';
+ }
+}
+
+export interface PanoramaQuery {
+ commit?: string;
+ base?: string;
+ head?: string;
+ root?: string;
+ depth?: number;
+ maxNodes?: number;
+ highlight?: 'trace';
+ traceSymbols?: string[];
+ traceEntryPoints?: string[];
+}
+
+function verifyCommit(clonePath: string, hash: string): void {
+ try {
+ git(['rev-parse', '--verify', hash], { cwd: clonePath });
+ } catch {
+ throw new PanoramaError(`Commit not found: ${hash}`, 404);
+ }
+}
+
+async function loadSnapshot(
+ registry: RepoRegistry,
+ repoId: string,
+ commitHash: string,
+) {
+ const ref = registry.get(repoId);
+ if (!ref) throw new PanoramaError('Repository not found', 404);
+ verifyCommit(ref.clonePath, commitHash);
+
+ const cacheRoot = registry.getCacheRoot();
+ const analyzerVersion = readAnalyzerVersion(resolveMonorepoRoot());
+ try {
+ return await getOrBuildSnapshot({
+ repoId,
+ commitHash,
+ clonePath: ref.clonePath,
+ cacheRoot,
+ analyzerVersion,
+ });
+ } catch (err: unknown) {
+ if (err instanceof SnapshotTimeoutError) throw new PanoramaError(err.message, 504);
+ if (err instanceof SnapshotEmptyError) throw new PanoramaError(err.message, 422);
+ if (err instanceof SnapshotBuildError) {
+ throw new PanoramaError(`Snapshot build failed: ${err.message}`, 500);
+ }
+ throw new PanoramaError(
+ `Snapshot build failed: ${err instanceof Error ? err.message : String(err)}`,
+ 500,
+ );
+ }
+}
+
+function readSourceSnippet(
+ clonePath: string,
+ commit: string,
+ node: CodeNode,
+ contextLines = 3,
+): string {
+ try {
+ const raw = git(['show', `${commit}:${node.filePath}`], { cwd: clonePath });
+ const lines = raw.split('\n');
+ const start = Math.max(0, node.startLine - 1 - contextLines);
+ const end = Math.min(lines.length, node.endLine + contextLines);
+ return lines.slice(start, end).join('\n');
+ } catch {
+ return node.signature ?? node.qualifiedName;
+ }
+}
+
+export async function getPanorama(
+ registry: RepoRegistry,
+ repoId: string,
+ query: PanoramaQuery,
+): Promise {
+ const ref = registry.get(repoId);
+ if (!ref) throw new PanoramaError('Repository not found', 404);
+
+ const depth = query.depth ?? 3;
+ const maxNodes = query.maxNodes ?? 200;
+ const root = query.root?.trim();
+
+ let graph: PanoramaGraph;
+
+ if (query.base && query.head) {
+ verifyCommit(ref.clonePath, query.base);
+ verifyCommit(ref.clonePath, query.head);
+ const [baseSnap, headSnap] = await Promise.all([
+ loadSnapshot(registry, repoId, query.base),
+ loadSnapshot(registry, repoId, query.head),
+ ]);
+ graph = buildDeltaPanoramaGraph(repoId, baseSnap, headSnap, {
+ rootQuery: root,
+ maxDepth: depth,
+ maxNodes,
+ deltaOnly: true,
+ seedEntryPoints: query.traceEntryPoints,
+ });
+ } else {
+ const commit = query.commit?.trim();
+ if (!commit) {
+ throw new PanoramaError('Query parameter commit or base+head is required', 400);
+ }
+ const snap = await loadSnapshot(registry, repoId, commit);
+ const budget = resolvePanoramaBudget(snap.nodes.length);
+ graph = buildPanoramaGraph(repoId, snap, {
+ rootQuery: root,
+ maxDepth: depth,
+ maxNodes: query.maxNodes ?? budget.maxNodes,
+ entryLimit: budget.entryLimit,
+ });
+ }
+
+ if (query.highlight === 'trace' && (query.traceSymbols?.length || query.traceEntryPoints?.length)) {
+ const headCommit = query.head ?? query.commit;
+ if (!headCommit) return graph;
+ const snap = await loadSnapshot(registry, repoId, headCommit);
+ const highlighted = applyTraceHighlight(snap, graph.nodes, graph.edges, {
+ symbols: query.traceSymbols,
+ entryPoints: query.traceEntryPoints,
+ symbolQueries: query.traceSymbols,
+ });
+ graph = {
+ ...graph,
+ nodes: highlighted.nodes,
+ edges: highlighted.edges,
+ pathConnected: highlighted.pathConnected,
+ pathMessage: highlighted.pathMessage,
+ };
+ }
+
+ return graph;
+}
+
+export async function enrichPanoramaNodes(
+ registry: RepoRegistry,
+ settings: SettingsStore,
+ repoId: string,
+ body: PanoramaEnrichRequest,
+): Promise {
+ const ref = registry.get(repoId);
+ if (!ref) throw new PanoramaError('Repository not found', 404);
+
+ const commit = body.commit?.trim();
+ if (!commit) throw new PanoramaError('commit is required', 400);
+
+ const nodeIds = (body.nodeIds ?? []).slice(0, 20);
+ if (nodeIds.length === 0) {
+ return { labels: {}, nonAuthoritative: true };
+ }
+
+ const snap = await loadSnapshot(registry, repoId, commit);
+ const provider = createProvider(settings.getProvider());
+ if (!provider.isConfigured() || provider.id === 'none') {
+ throw new PanoramaError(
+ 'Configure a Provider in Settings to generate node descriptions (non-authoritative).',
+ 400,
+ );
+ }
+
+ const labels: Record = {};
+ for (const id of nodeIds) {
+ const node = snap.nodes.find((n) => n.id === id) ?? resolveNodeQuery(snap, id);
+ if (!node) continue;
+ const snippet = readSourceSnippet(ref.clonePath, commit, node);
+ try {
+ const text = await provider.complete({
+ system:
+ 'You label code symbols for a developer panorama graph. Reply with ONE short phrase (under 12 words) describing the symbol role. No markdown.',
+ messages: [
+ {
+ role: 'user',
+ content: `Symbol: ${node.qualifiedName}\nKind: ${node.kind}\nFile: ${node.filePath}:${node.startLine}\nSignature: ${node.signature ?? 'n/a'}\n\nSource:\n${snippet}`,
+ },
+ ],
+ temperature: 0.2,
+ });
+ labels[node.id] = text.trim().slice(0, 120);
+ } catch {
+ labels[node.id] = node.kind;
+ }
+ }
+
+ return { labels, nonAuthoritative: true };
+}
diff --git a/packages/codedelta-server/src/services/trace-provider-output.ts b/packages/codedelta-server/src/services/trace-provider-output.ts
new file mode 100644
index 000000000..4817d2062
--- /dev/null
+++ b/packages/codedelta-server/src/services/trace-provider-output.ts
@@ -0,0 +1,182 @@
+import type { TraceCandidateCommit, TraceEvidenceItem } from '@codedelta/types';
+
+export type ProviderTraceConfidence = 'low' | 'medium' | 'high';
+
+export interface ProviderTraceJson {
+ directAnswer?: string;
+ directAnswerEvidenceRefs?: string[];
+ mostLikelyCommitHash?: string | null;
+ confidence?: ProviderTraceConfidence | string;
+ evolution?: Array<{
+ label?: string;
+ commitHash?: string;
+ summary?: string;
+ evidenceRefs?: string[];
+ }>;
+ uncertainty?: string[];
+ uncertaintyEvidenceRefs?: string[];
+ suggestedNextChecks?: string[];
+}
+
+export interface ValidatedProviderTrace {
+ directAnswer?: string;
+ directAnswerEvidenceRefs: string[];
+ mostLikelyCommitHash?: string;
+ confidence?: ProviderTraceConfidence;
+ evolution: Array<{
+ label: 'before' | 'candidate' | 'after' | 'current';
+ commitHash?: string;
+ summary: string;
+ evidenceRefs: string[];
+ }>;
+ uncertainty: string[];
+ uncertaintyEvidenceRefs: string[];
+ suggestedNextChecks: string[];
+}
+
+export interface ValidateProviderOptions {
+ candidates: TraceCandidateCommit[];
+ evidence: TraceEvidenceItem[];
+ allCommitHashes: string[];
+}
+
+const EVOLUTION_LABELS = new Set(['before', 'candidate', 'after', 'current']);
+
+/** Pull JSON object from raw model text (handles ```json fences). */
+export function extractJsonFromModelText(raw: string): unknown | null {
+ const trimmed = raw.trim();
+ if (!trimmed) return null;
+
+ const fenceMatch = trimmed.match(/```(?:json)?\s*([\s\S]*?)```/i);
+ const candidate = fenceMatch?.[1]?.trim() ?? trimmed;
+
+ try {
+ return JSON.parse(candidate);
+ } catch {
+ const start = candidate.indexOf('{');
+ const end = candidate.lastIndexOf('}');
+ if (start >= 0 && end > start) {
+ try {
+ return JSON.parse(candidate.slice(start, end + 1));
+ } catch {
+ return null;
+ }
+ }
+ return null;
+ }
+}
+
+function filterEvidenceRefs(ids: unknown, allowed: Set): string[] {
+ if (!Array.isArray(ids)) return [];
+ return ids.filter((id): id is string => typeof id === 'string' && allowed.has(id));
+}
+
+function isConfidence(v: unknown): v is ProviderTraceConfidence {
+ return v === 'low' || v === 'medium' || v === 'high';
+}
+
+/**
+ * Validate and sanitize provider JSON against known candidates and evidence.
+ * Drops out-of-scope fields; never invents commits or evidence ids.
+ */
+export function validateProviderTraceOutput(
+ raw: unknown,
+ options: ValidateProviderOptions,
+): { ok: true; value: ValidatedProviderTrace } | { ok: false; reason: string } {
+ if (!raw || typeof raw !== 'object') {
+ return { ok: false, reason: 'Provider output is not a JSON object' };
+ }
+
+ const parsed = raw as ProviderTraceJson;
+ const allowedEvidence = new Set(options.evidence.map((e) => e.id));
+ const allowedCommits = new Set(options.allCommitHashes);
+ const candidateHashes = new Set(options.candidates.map((c) => c.commit.hash));
+
+ const directAnswer =
+ typeof parsed.directAnswer === 'string' && parsed.directAnswer.trim()
+ ? parsed.directAnswer.trim()
+ : undefined;
+
+ const directAnswerEvidenceRefs = filterEvidenceRefs(parsed.directAnswerEvidenceRefs, allowedEvidence);
+
+ let mostLikelyCommitHash: string | undefined;
+ if (parsed.mostLikelyCommitHash === null || parsed.mostLikelyCommitHash === undefined) {
+ mostLikelyCommitHash = undefined;
+ } else if (typeof parsed.mostLikelyCommitHash === 'string' && candidateHashes.has(parsed.mostLikelyCommitHash)) {
+ mostLikelyCommitHash = parsed.mostLikelyCommitHash;
+ } else if (typeof parsed.mostLikelyCommitHash === 'string') {
+ return { ok: false, reason: 'mostLikelyCommitHash is not in candidate set' };
+ }
+
+ const confidence = isConfidence(parsed.confidence) ? parsed.confidence : undefined;
+
+ const evolution: ValidatedProviderTrace['evolution'] = [];
+ if (Array.isArray(parsed.evolution)) {
+ for (const step of parsed.evolution) {
+ if (!step || typeof step !== 'object') continue;
+ const label = typeof step.label === 'string' ? step.label : '';
+ if (!EVOLUTION_LABELS.has(label)) continue;
+ const summary = typeof step.summary === 'string' ? step.summary.trim() : '';
+ if (!summary) continue;
+ let commitHash: string | undefined;
+ if (typeof step.commitHash === 'string' && allowedCommits.has(step.commitHash)) {
+ commitHash = step.commitHash;
+ }
+ evolution.push({
+ label: label as ValidatedProviderTrace['evolution'][number]['label'],
+ commitHash,
+ summary,
+ evidenceRefs: filterEvidenceRefs(step.evidenceRefs, allowedEvidence),
+ });
+ }
+ }
+
+ const uncertainty = Array.isArray(parsed.uncertainty)
+ ? parsed.uncertainty.filter((u): u is string => typeof u === 'string' && u.trim().length > 0)
+ : [];
+
+ const uncertaintyEvidenceRefs = filterEvidenceRefs(parsed.uncertaintyEvidenceRefs, allowedEvidence);
+
+ const suggestedNextChecks = Array.isArray(parsed.suggestedNextChecks)
+ ? parsed.suggestedNextChecks.filter((s): s is string => typeof s === 'string' && s.trim().length > 0)
+ : [];
+
+ if (!directAnswer && !mostLikelyCommitHash && evolution.length === 0) {
+ return { ok: false, reason: 'Provider JSON contained no usable fields after validation' };
+ }
+
+ return {
+ ok: true,
+ value: {
+ directAnswer,
+ directAnswerEvidenceRefs,
+ mostLikelyCommitHash,
+ confidence,
+ evolution,
+ uncertainty,
+ uncertaintyEvidenceRefs,
+ suggestedNextChecks,
+ },
+ };
+}
+
+export function buildTraceProviderSystemPrompt(): string {
+ return [
+ 'You are an evidence-grounded release investigator for CodeDelta Trace View.',
+ 'Use ONLY the commits and evidence items provided in the user message.',
+ 'Never invent commits, files, symbols, behavior, or evidence ids.',
+ 'Return strict JSON only (no markdown), matching this schema:',
+ '{',
+ ' "directAnswer": string,',
+ ' "directAnswerEvidenceRefs": string[],',
+ ' "mostLikelyCommitHash": string | null,',
+ ' "confidence": "low" | "medium" | "high",',
+ ' "evolution": [{ "label": "before"|"candidate"|"after"|"current", "commitHash"?: string, "summary": string, "evidenceRefs": string[] }],',
+ ' "uncertainty": string[],',
+ ' "uncertaintyEvidenceRefs": string[],',
+ ' "suggestedNextChecks": string[]',
+ '}',
+ 'Every conclusion must cite evidenceRefs that exist in the input evidence list.',
+ 'If evidence is insufficient, say so in uncertainty and keep confidence low.',
+ ].join('\n');
+}
diff --git a/packages/codedelta-server/src/services/trace.ts b/packages/codedelta-server/src/services/trace.ts
new file mode 100644
index 000000000..7b28a8b2a
--- /dev/null
+++ b/packages/codedelta-server/src/services/trace.ts
@@ -0,0 +1,405 @@
+import { createProvider } from '@codedelta/provider-runtime';
+import { findCandidateCommits, type TraceCommitContext } from '@codedelta/trace-engine';
+import { getCommitDetail, listCommits } from '@codedelta/repo-manager';
+import type {
+ CommitInfo,
+ CompareResponse,
+ TraceAnswer,
+ TraceCandidateCommit,
+ TraceEvidenceItem,
+ TraceQuestion,
+} from '@codedelta/types';
+import { compareCommits, CompareError } from './compare';
+import {
+ buildTraceProviderSystemPrompt,
+ extractJsonFromModelText,
+ validateProviderTraceOutput,
+} from './trace-provider-output';
+import { RepoRegistry, SettingsStore } from '../store/repo-registry';
+
+export class TraceError extends Error {
+ constructor(
+ message: string,
+ public readonly statusCode: number,
+ ) {
+ super(message);
+ this.name = 'TraceError';
+ }
+}
+
+function clamp(n: number, min: number, max: number): number {
+ return Math.max(min, Math.min(max, n));
+}
+
+async function mapWithConcurrency(
+ items: T[],
+ limit: number,
+ fn: (item: T) => Promise,
+): Promise {
+ const queue = [...items];
+ const workers = Array.from({ length: Math.min(limit, queue.length) }, async () => {
+ for (let item = queue.shift(); item !== undefined; item = queue.shift()) {
+ await fn(item);
+ }
+ });
+ await Promise.all(workers);
+}
+
+function confidenceFromCandidates(candidates: TraceCandidateCommit[], commitCount: number): 'low' | 'medium' | 'high' {
+ if (commitCount < 3) return 'low';
+ const top = candidates[0]?.relevanceScore ?? 0;
+ const second = candidates[1]?.relevanceScore ?? 0;
+ if (top >= 65 && top - second >= 10) return 'high';
+ if (top >= 35) return 'medium';
+ return 'low';
+}
+
+export async function runTrace(
+ registry: RepoRegistry,
+ settings: SettingsStore,
+ query: TraceQuestion,
+): Promise {
+ const ref = registry.get(query.repoId);
+ if (!ref) throw new TraceError('Repository not found', 404);
+ if (!query.question.trim()) throw new TraceError('Question is required', 400);
+
+ const branch = query.branch ?? ref.defaultBranch;
+ const commitLimit = clamp(query.commitLimit ?? 50, 10, 200);
+ const includeDiffEvidence = query.includeDiffEvidence ?? true;
+
+ let commits: CommitInfo[] = [];
+ try {
+ commits = listCommits(ref.clonePath, branch, { limit: commitLimit });
+ } catch (err) {
+ throw new TraceError(`Candidate retrieval failed: ${err instanceof Error ? err.message : String(err)}`, 500);
+ }
+ if (commits.length === 0) throw new TraceError('No commits available', 404);
+
+ const contexts: TraceCommitContext[] = commits.map((commit) => ({
+ commit,
+ changedFiles: getCommitDetail(ref.clonePath, commit.hash).changedFiles,
+ }));
+
+ const candidates = findCandidateCommits(contexts, query.question, 8);
+
+ // Pre-compute candidate compares with bounded parallelism (snapshot builds are
+ // the expensive part; the singleflight in snapshot-manager dedupes overlapping
+ // parent commits across candidates). Evidence assembly below stays in
+ // deterministic candidate order.
+ const compareResults = new Map();
+ await mapWithConcurrency(
+ candidates.filter((c) => c.commit.parents[0]),
+ 3,
+ async (candidate) => {
+ const prev = candidate.commit.parents[0];
+ if (!prev) return;
+ try {
+ const cmp = await compareCommits(registry, query.repoId, prev, candidate.commit.hash);
+ compareResults.set(candidate.commit.hash, { cmp });
+ } catch (err) {
+ const detail =
+ err instanceof CompareError ? err.message : err instanceof Error ? err.message : String(err);
+ compareResults.set(candidate.commit.hash, { error: detail });
+ }
+ },
+ );
+
+ const evidence: TraceEvidenceItem[] = [];
+ const uncertainty: string[] = [];
+ const uncertaintyEvidenceRefs: string[] = [];
+ const files = new Set();
+ const symbols = new Set();
+ const entryPoints = new Set();
+ const riskTags = new Set();
+
+ for (const candidate of candidates) {
+ const commitHash = candidate.commit.hash;
+ evidence.push({
+ id: `ev-${commitHash}-message`,
+ kind: 'commit-message',
+ commitHash,
+ title: 'Commit message match',
+ detail: candidate.commit.message,
+ score: candidate.relevanceScore,
+ });
+
+ for (const changed of candidate.changedFiles.slice(0, 20)) {
+ files.add(changed.path);
+ evidence.push({
+ id: `ev-${commitHash}-file-${changed.path}`,
+ kind: 'changed-file',
+ commitHash,
+ title: `${changed.status}: ${changed.path}`,
+ detail: `Changed file ${changed.path}`,
+ file: changed.path,
+ });
+ }
+
+ const prev = candidate.commit.parents[0];
+ if (!prev) {
+ const noPrevId = `ev-${commitHash}-delta-unavailable-noparent`;
+ evidence.push({
+ id: noPrevId,
+ kind: 'delta-unavailable',
+ commitHash,
+ title: 'Delta unavailable',
+ detail: 'No parent commit available for previous -> candidate comparison',
+ });
+ uncertainty.push(`No previous commit for ${candidate.commit.shortHash}; delta comparison unavailable`);
+ uncertaintyEvidenceRefs.push(noPrevId);
+ continue;
+ }
+ candidate.previousCommitHash = prev;
+ const result = compareResults.get(candidate.commit.hash);
+ if (result?.cmp) {
+ const cmp = result.cmp;
+ candidate.impactSummary = cmp.impact;
+ candidate.deltaSummary = cmp.deltaSummary;
+
+ if (cmp.deltaSummary) {
+ evidence.push({
+ id: `ev-${commitHash}-delta-summary`,
+ kind: 'delta-summary',
+ commitHash,
+ title: 'Delta summary',
+ detail: cmp.deltaSummary.overview.join(' | '),
+ });
+ }
+
+ for (const n of cmp.graphDiff.modifiedNodes.slice(0, 12)) {
+ symbols.add(n.after.qualifiedName);
+ evidence.push({
+ id: `ev-${commitHash}-symbol-${n.after.id}`,
+ kind: 'changed-symbol',
+ commitHash,
+ title: `${n.after.name} modified`,
+ detail: n.changes.join(', '),
+ file: n.after.filePath,
+ symbol: n.after.qualifiedName,
+ });
+ }
+
+ for (const e of cmp.graphDiff.addedEdges.slice(0, 8)) {
+ evidence.push({
+ id: `ev-${commitHash}-edge-${e.kind}-${e.source}-${e.target}`,
+ kind: 'edge-change',
+ commitHash,
+ title: `Edge + ${e.kind}`,
+ detail: `${e.source} -> ${e.target}`,
+ });
+ }
+
+ for (const tag of cmp.impact.riskTags) {
+ riskTags.add(tag);
+ evidence.push({
+ id: `ev-${commitHash}-risk-${tag}`,
+ kind: 'risk-tag',
+ commitHash,
+ title: `Risk tag: ${tag}`,
+ detail: `Impact model matched risk tag ${tag}`,
+ });
+ }
+
+ for (const ep of cmp.impact.impactedEntryPoints.slice(0, 10)) {
+ entryPoints.add(ep);
+ evidence.push({
+ id: `ev-${commitHash}-entry-${ep}`,
+ kind: 'entry-point',
+ commitHash,
+ title: 'Impacted entry point',
+ detail: ep,
+ symbol: ep,
+ });
+ }
+
+ if (includeDiffEvidence) {
+ const topFile = cmp.graphDiff.changedFiles[0]?.path;
+ if (topFile) {
+ evidence.push({
+ id: `ev-${commitHash}-codediff-${topFile}`,
+ kind: 'code-diff',
+ commitHash,
+ title: `Code diff available: ${topFile}`,
+ detail: `Open Delta View diff for ${topFile}`,
+ file: topFile,
+ });
+ }
+ }
+ } else {
+ const unavailableId = `ev-${commitHash}-delta-unavailable`;
+ const detail = result?.error ?? 'Comparison unavailable';
+ evidence.push({
+ id: unavailableId,
+ kind: 'delta-unavailable',
+ commitHash,
+ title: 'Delta unavailable',
+ detail,
+ });
+ uncertainty.push(`Delta unavailable for ${candidate.commit.shortHash}: ${detail}`);
+ uncertaintyEvidenceRefs.push(unavailableId);
+ }
+ }
+
+ const confidence = confidenceFromCandidates(candidates, commits.length);
+ const mostLikely = candidates[0]?.commit;
+ const directAnswer =
+ mostLikely && candidates[0]
+ ? `Most likely related commit is ${mostLikely.shortHash}: ${mostLikely.message}. Evidence score ${candidates[0].relevanceScore}.`
+ : 'No strong candidate found from commit history.';
+ const directAnswerEvidenceRefs = mostLikely ? [`ev-${mostLikely.hash}-message`] : [];
+
+ const evolution = mostLikely
+ ? [
+ {
+ label: 'before' as const,
+ commitHash: candidates[0]?.previousCommitHash,
+ summary: 'State before candidate change.',
+ evidenceRefs: candidates[0]?.previousCommitHash ? [`ev-${mostLikely.hash}-delta-summary`] : [],
+ },
+ {
+ label: 'candidate' as const,
+ commitHash: mostLikely.hash,
+ summary: `Candidate commit ${mostLikely.shortHash} introduces the strongest lexical and structural signals.`,
+ evidenceRefs: [`ev-${mostLikely.hash}-message`],
+ },
+ {
+ label: 'current' as const,
+ commitHash: commits[0]?.hash,
+ summary: 'Current branch head after subsequent changes.',
+ evidenceRefs: [],
+ },
+ ]
+ : [
+ {
+ label: 'current' as const,
+ commitHash: commits[0]?.hash,
+ summary: 'Insufficient evidence to identify an introducing commit.',
+ evidenceRefs: [],
+ },
+ ];
+
+ if (commits.length < 3) {
+ uncertainty.push('Commit history is too short for reliable origin tracing');
+ }
+
+ const suggestedNextChecks = [
+ ...(candidates[0]?.previousCommitHash
+ ? [`Open Delta View: base=${candidates[0].previousCommitHash} head=${candidates[0].commit.hash}`]
+ : []),
+ 'Inspect top changed files from candidate commits in diff viewer',
+ 'Cross-check uncertain items with runtime logs or tests',
+ ];
+
+ const providerConfig = settings.getProvider();
+ const provider = createProvider(providerConfig);
+ const answer: TraceAnswer = {
+ question: query.question,
+ directAnswer,
+ directAnswerEvidenceRefs,
+ mostLikelyCommit: mostLikely,
+ candidates,
+ evidence,
+ impactRadius: {
+ files: [...files].sort(),
+ symbols: [...symbols].sort(),
+ entryPoints: [...entryPoints].sort(),
+ riskTags: [...riskTags].sort(),
+ },
+ evolution,
+ confidence,
+ uncertainty: [...new Set(uncertainty)],
+ uncertaintyEvidenceRefs: [...new Set(uncertaintyEvidenceRefs)],
+ suggestedNextChecks,
+ provider: {
+ type: provider.id,
+ model: providerConfig.model,
+ used: false,
+ },
+ };
+
+ if (provider.id !== 'none' && provider.isConfigured()) {
+ const allCommitHashes = commits.map((c) => c.hash);
+ try {
+ const modelText = await provider.complete({
+ system: buildTraceProviderSystemPrompt(),
+ messages: [
+ {
+ role: 'user',
+ content: JSON.stringify(
+ {
+ question: query.question,
+ branch,
+ allowedCommitHashes: allCommitHashes,
+ allowedEvidenceIds: evidence.map((e) => e.id),
+ candidates: candidates.slice(0, 5).map((c) => ({
+ hash: c.commit.hash,
+ message: c.commit.message,
+ score: c.relevanceScore,
+ reasons: c.reasons,
+ previousCommitHash: c.previousCommitHash,
+ })),
+ evidence: evidence.slice(0, 30).map((e) => ({
+ id: e.id,
+ kind: e.kind,
+ commitHash: e.commitHash,
+ title: e.title,
+ detail: e.detail.length > 240 ? `${e.detail.slice(0, 240)}…` : e.detail,
+ file: e.file,
+ symbol: e.symbol,
+ })),
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ temperature: 0.1,
+ });
+ const rawJson = extractJsonFromModelText(modelText);
+ const validated = validateProviderTraceOutput(rawJson, {
+ candidates,
+ evidence,
+ allCommitHashes,
+ });
+ if (!validated.ok) {
+ answer.uncertainty.push(`Provider output rejected: ${validated.reason}`);
+ answer.provider = {
+ type: provider.id,
+ model: providerConfig.model,
+ used: true,
+ nonAuthoritativeText: modelText,
+ };
+ } else {
+ const v = validated.value;
+ if (v.directAnswer) answer.directAnswer = v.directAnswer;
+ if (v.directAnswerEvidenceRefs.length) answer.directAnswerEvidenceRefs = v.directAnswerEvidenceRefs;
+ if (v.mostLikelyCommitHash) {
+ answer.mostLikelyCommit = candidates.find((c) => c.commit.hash === v.mostLikelyCommitHash)?.commit;
+ }
+ if (v.confidence) answer.confidence = v.confidence;
+ if (v.evolution.length) answer.evolution = v.evolution;
+ if (v.uncertainty.length) {
+ answer.uncertainty = [...new Set([...answer.uncertainty, ...v.uncertainty])];
+ }
+ if (v.uncertaintyEvidenceRefs.length) {
+ answer.uncertaintyEvidenceRefs = [
+ ...new Set([...(answer.uncertaintyEvidenceRefs ?? []), ...v.uncertaintyEvidenceRefs]),
+ ];
+ }
+ if (v.suggestedNextChecks.length) answer.suggestedNextChecks = v.suggestedNextChecks;
+ answer.provider = {
+ type: provider.id,
+ model: providerConfig.model,
+ used: true,
+ };
+ }
+ } catch (err) {
+ answer.uncertainty.push(
+ `Provider failed; returned deterministic evidence-only answer: ${err instanceof Error ? err.message : String(err)}`,
+ );
+ }
+ }
+
+ return answer;
+}
+
diff --git a/packages/codedelta-server/src/services/wiki.ts b/packages/codedelta-server/src/services/wiki.ts
new file mode 100644
index 000000000..3e1545171
--- /dev/null
+++ b/packages/codedelta-server/src/services/wiki.ts
@@ -0,0 +1,427 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import { execFileSync } from 'child_process';
+import { createProvider } from '@codedelta/provider-runtime';
+import { git } from '@codedelta/repo-manager';
+import {
+ getOrBuildSnapshot,
+ readAnalyzerVersion,
+ resolveMonorepoRoot,
+ SnapshotBuildError,
+ SnapshotEmptyError,
+ SnapshotTimeoutError,
+ SnapshotTooLargeError,
+} from '@codedelta/snapshot-manager';
+import type {
+ CodeGraphSnapshot,
+ WikiAskAnswer,
+ WikiAskRequest,
+ WikiPageContent,
+ WikiStatus,
+ WikiToc,
+} from '@codedelta/types';
+import {
+ buildSectionContext,
+ buildWikiAskSystemPrompt,
+ buildWikiAssetUrl,
+ buildWikiPageSystemPrompt,
+ buildWikiPageUserPayload,
+ citationsFromEvidence,
+ composeWikiPage,
+ extractJsonObject,
+ normalizeWikiAssetPath,
+ planWikiToc,
+ prepareAskRetrieval,
+ rewriteWikiAssetUrls,
+ validateWikiAskOutput,
+ validateWikiPageOutput,
+ WIKI_VERSION,
+ type ReadSource,
+} from '@codedelta/wiki-engine';
+import type { JobStore } from '../jobs';
+import { RepoRegistry, SettingsStore } from '../store/repo-registry';
+
+export class WikiError extends Error {
+ constructor(
+ message: string,
+ public readonly statusCode: number,
+ ) {
+ super(message);
+ this.name = 'WikiError';
+ }
+}
+
+interface WikiMeta {
+ llmUsed: boolean;
+ generatedAt: string;
+}
+
+function wikiDir(cacheRoot: string, repoId: string, commitHash: string): string {
+ return path.join(cacheRoot, 'wiki', repoId, commitHash, WIKI_VERSION);
+}
+
+function writeJsonAtomic(filePath: string, value: unknown): void {
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
+ fs.writeFileSync(tmpPath, JSON.stringify(value, null, 2) + '\n', 'utf8');
+ fs.renameSync(tmpPath, filePath);
+}
+
+function readJson(filePath: string): T | null {
+ if (!fs.existsSync(filePath)) return null;
+ try {
+ return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;
+ } catch {
+ return null;
+ }
+}
+
+function verifyCommit(clonePath: string, hash: string): void {
+ try {
+ git(['rev-parse', '--verify', hash], { cwd: clonePath });
+ } catch {
+ throw new WikiError(`Commit not found: ${hash}`, 404);
+ }
+}
+
+function requireRepo(registry: RepoRegistry, repoId: string) {
+ const ref = registry.get(repoId);
+ if (!ref) throw new WikiError('Repository not found', 404);
+ return ref;
+}
+
+async function loadSnapshot(
+ registry: RepoRegistry,
+ repoId: string,
+ commitHash: string,
+): Promise {
+ const ref = requireRepo(registry, repoId);
+ verifyCommit(ref.clonePath, commitHash);
+ try {
+ return await getOrBuildSnapshot({
+ repoId,
+ commitHash,
+ clonePath: ref.clonePath,
+ cacheRoot: registry.getCacheRoot(),
+ analyzerVersion: readAnalyzerVersion(resolveMonorepoRoot()),
+ });
+ } catch (err: unknown) {
+ if (err instanceof SnapshotTimeoutError) throw new WikiError(err.message, 504);
+ if (err instanceof SnapshotTooLargeError) throw new WikiError(err.message, 413);
+ if (err instanceof SnapshotEmptyError) throw new WikiError(err.message, 422);
+ if (err instanceof SnapshotBuildError) {
+ throw new WikiError(`Snapshot build failed: ${err.message}`, 500);
+ }
+ throw new WikiError(
+ `Snapshot build failed: ${err instanceof Error ? err.message : String(err)}`,
+ 500,
+ );
+ }
+}
+
+/** File reader at a commit via `git show`, memoized per generation/ask run. */
+function makeReadSource(clonePath: string, commitHash: string): ReadSource {
+ const cache = new Map();
+ return (filePath: string) => {
+ if (cache.has(filePath)) return cache.get(filePath) ?? null;
+ let content: string | null;
+ try {
+ content = git(['show', `${commitHash}:${filePath}`], { cwd: clonePath });
+ } catch {
+ content = null;
+ }
+ cache.set(filePath, content);
+ return content;
+ };
+}
+
+function wikiAssetResolver(repoId: string, commitHash: string): (relativePath: string) => string {
+ return (relativePath) => buildWikiAssetUrl(repoId, commitHash, relativePath);
+}
+
+const WIKI_ASSET_MIME: Record = {
+ '.png': 'image/png',
+ '.jpg': 'image/jpeg',
+ '.jpeg': 'image/jpeg',
+ '.gif': 'image/gif',
+ '.webp': 'image/webp',
+ '.svg': 'image/svg+xml',
+ '.ico': 'image/x-icon',
+};
+
+function mimeForWikiAsset(filePath: string): string {
+ const ext = path.posix.extname(filePath).toLowerCase();
+ return WIKI_ASSET_MIME[ext] ?? 'application/octet-stream';
+}
+
+/** Serve a repository file at a commit (README images, etc.). */
+export function getWikiAsset(
+ registry: RepoRegistry,
+ repoId: string,
+ commitHash: string,
+ fileInput: string,
+): { body: Buffer; contentType: string } {
+ const ref = requireRepo(registry, repoId);
+ verifyCommit(ref.clonePath, commitHash);
+ let filePath: string;
+ try {
+ filePath = normalizeWikiAssetPath(fileInput);
+ } catch {
+ throw new WikiError('Invalid asset path', 400);
+ }
+ try {
+ const body = execFileSync('git', ['show', `${commitHash}:${filePath}`], {
+ cwd: ref.clonePath,
+ encoding: 'buffer',
+ maxBuffer: 20 * 1024 * 1024,
+ }) as Buffer;
+ return { body, contentType: mimeForWikiAsset(filePath) };
+ } catch {
+ throw new WikiError(`Asset not found at commit: ${filePath}`, 404);
+ }
+}
+
+function wikiJobKey(repoId: string, commitHash: string): string {
+ return `wiki\u0000${repoId}\u0000${commitHash}`;
+}
+
+export function getWikiStatus(
+ registry: RepoRegistry,
+ jobs: JobStore,
+ repoId: string,
+ commitHash: string,
+): WikiStatus {
+ requireRepo(registry, repoId);
+
+ const job = jobs.getActiveByKey(wikiJobKey(repoId, commitHash));
+ if (job) {
+ return {
+ state: 'generating',
+ commitHash,
+ jobId: job.id,
+ totalSections: job.progress.total,
+ completedSections: job.progress.completed,
+ currentSection: job.progress.phase,
+ };
+ }
+
+ const dir = wikiDir(registry.getCacheRoot(), repoId, commitHash);
+ const meta = readJson(path.join(dir, 'meta.json'));
+ const toc = readJson(path.join(dir, 'toc.json'));
+ if (meta && toc) {
+ return {
+ state: 'ready',
+ commitHash,
+ totalSections: toc.sections.length,
+ completedSections: toc.sections.length,
+ llmUsed: meta.llmUsed,
+ generatedAt: meta.generatedAt,
+ };
+ }
+ return { state: 'absent', commitHash };
+}
+
+export function startWikiGeneration(
+ registry: RepoRegistry,
+ settings: SettingsStore,
+ jobs: JobStore,
+ repoId: string,
+ commitHash: string,
+): { jobId: string; alreadyReady: boolean } {
+ const ref = requireRepo(registry, repoId);
+ verifyCommit(ref.clonePath, commitHash);
+
+ const status = getWikiStatus(registry, jobs, repoId, commitHash);
+ if (status.state === 'ready') {
+ return { jobId: '', alreadyReady: true };
+ }
+
+ const job = jobs.start('wiki-generate', wikiJobKey(repoId, commitHash), async (report) => {
+ report({ phase: 'snapshot' });
+ const snapshot = await loadSnapshot(registry, repoId, commitHash);
+
+ report({ phase: 'toc' });
+ const toc = planWikiToc(snapshot);
+ const dir = wikiDir(registry.getCacheRoot(), repoId, commitHash);
+ writeJsonAtomic(path.join(dir, 'toc.json'), toc);
+
+ const providerConfig = settings.getProvider();
+ const provider = createProvider(providerConfig);
+ const useLlm = provider.id !== 'none' && provider.isConfigured();
+ const readSource = makeReadSource(ref.clonePath, commitHash);
+ const resolveReadmeAssetUrl = wikiAssetResolver(repoId, commitHash);
+
+ report({ total: toc.sections.length, completed: 0 });
+ let completed = 0;
+ let llmUsedAnywhere = false;
+
+ for (const section of toc.sections) {
+ report({ phase: section.title, completed });
+ const context = buildSectionContext(snapshot, section, readSource, {
+ resolveReadmeAssetUrl,
+ });
+
+ let narrative: string | undefined;
+ if (useLlm) {
+ try {
+ const modelText = await provider.complete({
+ system: buildWikiPageSystemPrompt(),
+ messages: [{ role: 'user', content: buildWikiPageUserPayload(context) }],
+ temperature: 0.2,
+ });
+ const validated = validateWikiPageOutput(extractJsonObject(modelText), context.evidence);
+ if (validated.ok) {
+ narrative = validated.value.narrative;
+ llmUsedAnywhere = true;
+ }
+ } catch {
+ // Deterministic page still ships; the section just has no narrative.
+ }
+ }
+
+ const { markdown, citations } = composeWikiPage(snapshot, context, narrative);
+ const page: WikiPageContent = {
+ sectionId: section.id,
+ title: section.title,
+ markdown,
+ citations,
+ llmUsed: narrative !== undefined,
+ generatedAt: new Date().toISOString(),
+ };
+ writeJsonAtomic(path.join(dir, 'pages', `${section.id}.json`), page);
+ completed += 1;
+ report({ completed });
+ }
+
+ const meta: WikiMeta = { llmUsed: llmUsedAnywhere, generatedAt: new Date().toISOString() };
+ writeJsonAtomic(path.join(dir, 'meta.json'), meta);
+ });
+
+ return { jobId: job.id, alreadyReady: false };
+}
+
+export function getWikiToc(registry: RepoRegistry, repoId: string, commitHash: string): WikiToc {
+ requireRepo(registry, repoId);
+ const toc = readJson(
+ path.join(wikiDir(registry.getCacheRoot(), repoId, commitHash), 'toc.json'),
+ );
+ if (!toc) {
+ throw new WikiError('Wiki not generated for this commit yet. POST /wiki/generate first.', 404);
+ }
+ return toc;
+}
+
+export function getWikiPage(
+ registry: RepoRegistry,
+ repoId: string,
+ commitHash: string,
+ sectionId: string,
+): WikiPageContent {
+ requireRepo(registry, repoId);
+ if (!/^[a-z0-9-]+$/.test(sectionId)) {
+ throw new WikiError('Invalid section id', 400);
+ }
+ const page = readJson(
+ path.join(wikiDir(registry.getCacheRoot(), repoId, commitHash), 'pages', `${sectionId}.json`),
+ );
+ if (!page) {
+ throw new WikiError('Wiki page not found. Generate the wiki for this commit first.', 404);
+ }
+ const resolveReadmeAssetUrl = wikiAssetResolver(repoId, commitHash);
+ return {
+ ...page,
+ markdown: rewriteWikiAssetUrls(page.markdown, resolveReadmeAssetUrl),
+ };
+}
+
+export async function askWiki(
+ registry: RepoRegistry,
+ settings: SettingsStore,
+ repoId: string,
+ body: WikiAskRequest,
+): Promise {
+ const ref = requireRepo(registry, repoId);
+ const commitHash = body.commit?.trim();
+ if (!commitHash) throw new WikiError('commit is required', 400);
+ const question = body.question?.trim();
+ if (!question) throw new WikiError('question is required', 400);
+
+ const providerConfig = settings.getProvider();
+ const provider = createProvider(providerConfig);
+ if (provider.id === 'none' || !provider.isConfigured()) {
+ throw new WikiError(
+ 'Wiki Ask requires a configured LLM provider. Open Settings → Provider Settings (Codex OAuth, OpenAI, or compatible).',
+ 503,
+ );
+ }
+
+ const snapshot = await loadSnapshot(registry, repoId, commitHash);
+ const readSource = makeReadSource(ref.clonePath, commitHash);
+ const retrieval = prepareAskRetrieval(snapshot, question, readSource);
+
+ const answer: WikiAskAnswer = {
+ question,
+ answer: '',
+ citations: [],
+ evidence: retrieval.evidence,
+ confidence: 'low',
+ provider: { type: provider.id, model: providerConfig.model, used: false },
+ };
+
+ try {
+ const history = (body.history ?? []).slice(-6);
+ const modelText = await provider.complete({
+ system: buildWikiAskSystemPrompt(),
+ messages: [
+ ...history.map((turn) => ({ role: turn.role, content: turn.content })),
+ {
+ role: 'user' as const,
+ content: JSON.stringify(
+ {
+ question,
+ evidence: retrieval.evidence.map((e) => ({
+ id: e.id,
+ kind: e.kind,
+ title: e.title,
+ detail: e.detail.length > 1500 ? `${e.detail.slice(0, 1500)}…` : e.detail,
+ file: e.file,
+ symbol: e.symbol,
+ lines: e.startLine !== undefined ? `${e.startLine}-${e.endLine}` : undefined,
+ })),
+ },
+ null,
+ 2,
+ ),
+ },
+ ],
+ temperature: 0.1,
+ });
+ const validated = validateWikiAskOutput(extractJsonObject(modelText), retrieval.evidence);
+ if (validated.ok) {
+ answer.answer = validated.value.answer;
+ answer.citations = citationsFromEvidence(validated.value.citationIds, retrieval.evidence);
+ answer.confidence = validated.value.confidence;
+ answer.provider = { type: provider.id, model: providerConfig.model, used: true };
+ } else {
+ answer.answer =
+ 'The model returned a response that could not be validated against the evidence whitelist. Try rephrasing with a concrete symbol, file, or module name.';
+ answer.provider = {
+ type: provider.id,
+ model: providerConfig.model,
+ used: true,
+ nonAuthoritativeText: modelText,
+ };
+ }
+ } catch (err) {
+ throw new WikiError(
+ `Wiki Ask failed: ${err instanceof Error ? err.message : String(err)}`,
+ 502,
+ );
+ }
+
+ if (!answer.answer.trim()) {
+ throw new WikiError('Wiki Ask returned an empty answer from the provider.', 502);
+ }
+
+ return answer;
+}
diff --git a/packages/codedelta-server/src/store/repo-registry.ts b/packages/codedelta-server/src/store/repo-registry.ts
new file mode 100644
index 000000000..b9a7aafd9
--- /dev/null
+++ b/packages/codedelta-server/src/store/repo-registry.ts
@@ -0,0 +1,99 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import type { ModelProviderConfig, RepoRef } from '@codedelta/types';
+import { getCacheRoot, getRegistryPath, getSettingsPath } from '@codedelta/repo-manager';
+
+const DEFAULT_PROVIDER: ModelProviderConfig = { kind: 'none' };
+
+/** Atomic JSON write: temp file + rename so a crash never leaves a half-written file. */
+function writeJsonAtomic(filePath: string, value: unknown): void {
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
+ fs.writeFileSync(tmpPath, JSON.stringify(value, null, 2) + '\n', 'utf8');
+ fs.renameSync(tmpPath, filePath);
+}
+
+export class RepoRegistry {
+ private repos = new Map();
+ private readonly cacheRoot: string;
+ private readonly registryPath: string;
+
+ constructor(cacheRoot?: string) {
+ this.cacheRoot = cacheRoot ?? getCacheRoot();
+ this.registryPath = getRegistryPath(this.cacheRoot);
+ this.load();
+ }
+
+ getCacheRoot(): string {
+ return this.cacheRoot;
+ }
+
+ list(): RepoRef[] {
+ return Array.from(this.repos.values()).sort(
+ (a, b) => new Date(b.importedAt).getTime() - new Date(a.importedAt).getTime(),
+ );
+ }
+
+ get(id: string): RepoRef | undefined {
+ return this.repos.get(id);
+ }
+
+ add(ref: RepoRef): RepoRef {
+ this.repos.set(ref.id, ref);
+ this.save();
+ return ref;
+ }
+
+ private load(): void {
+ fs.mkdirSync(this.cacheRoot, { recursive: true });
+ if (!fs.existsSync(this.registryPath)) {
+ fs.writeFileSync(this.registryPath, '[]\n', 'utf8');
+ return;
+ }
+ try {
+ const raw = fs.readFileSync(this.registryPath, 'utf8');
+ const list = JSON.parse(raw) as RepoRef[];
+ for (const ref of list) {
+ this.repos.set(ref.id, ref);
+ }
+ } catch {
+ this.repos.clear();
+ }
+ }
+
+ private save(): void {
+ writeJsonAtomic(this.registryPath, this.list());
+ }
+}
+
+export class SettingsStore {
+ private readonly settingsPath: string;
+ private config: ModelProviderConfig;
+
+ constructor(cacheRoot?: string) {
+ const root = cacheRoot ?? getCacheRoot();
+ this.settingsPath = getSettingsPath(root);
+ this.config = this.load();
+ }
+
+ getProvider(): ModelProviderConfig {
+ return { ...this.config };
+ }
+
+ setProvider(config: ModelProviderConfig): ModelProviderConfig {
+ this.config = { ...config };
+ writeJsonAtomic(this.settingsPath, this.config);
+ return this.getProvider();
+ }
+
+ private load(): ModelProviderConfig {
+ if (!fs.existsSync(this.settingsPath)) {
+ return { ...DEFAULT_PROVIDER };
+ }
+ try {
+ return JSON.parse(fs.readFileSync(this.settingsPath, 'utf8')) as ModelProviderConfig;
+ } catch {
+ return { ...DEFAULT_PROVIDER };
+ }
+ }
+}
diff --git a/packages/codedelta-server/tsconfig.json b/packages/codedelta-server/tsconfig.json
new file mode 100644
index 000000000..d8eed40e6
--- /dev/null
+++ b/packages/codedelta-server/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-snapshot-manager/__tests__/cache-paths.test.ts b/packages/codedelta-snapshot-manager/__tests__/cache-paths.test.ts
new file mode 100644
index 000000000..4799bfad7
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/__tests__/cache-paths.test.ts
@@ -0,0 +1,25 @@
+import * as path from 'path';
+import { afterEach, describe, expect, it } from 'vitest';
+import { resolveMonorepoRoot } from '../src/cache-paths';
+
+describe('resolveMonorepoRoot', () => {
+ const prev = process.env.CODEDELTA_MONOREPO_ROOT;
+
+ afterEach(() => {
+ if (prev === undefined) {
+ delete process.env.CODEDELTA_MONOREPO_ROOT;
+ } else {
+ process.env.CODEDELTA_MONOREPO_ROOT = prev;
+ }
+ });
+
+ it('uses CODEDELTA_MONOREPO_ROOT when set', () => {
+ process.env.CODEDELTA_MONOREPO_ROOT = '/tmp/codedelta-runtime';
+ expect(resolveMonorepoRoot()).toBe(path.resolve('/tmp/codedelta-runtime'));
+ });
+
+ it('falls back to monorepo relative path', () => {
+ delete process.env.CODEDELTA_MONOREPO_ROOT;
+ expect(resolveMonorepoRoot()).toBe(path.resolve(__dirname, '../../..'));
+ });
+});
diff --git a/packages/codedelta-snapshot-manager/__tests__/snapshot.test.ts b/packages/codedelta-snapshot-manager/__tests__/snapshot.test.ts
new file mode 100644
index 000000000..93af84610
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/__tests__/snapshot.test.ts
@@ -0,0 +1,59 @@
+import * as fs from 'fs';
+import * as os from 'os';
+import * as path from 'path';
+import { execFileSync } from 'child_process';
+import { afterEach, beforeEach, describe, expect, it } from 'vitest';
+import { snapshotFilePath } from '../src/cache-paths';
+import { buildFallbackSnapshot } from '../src/fallback-extractor';
+import { getOrBuildSnapshot, loadSnapshot, saveSnapshot } from '../src';
+
+function run(cmd: string, cwd: string): void {
+ execFileSync('sh', ['-c', cmd], { cwd, stdio: 'pipe' });
+}
+
+describe('snapshot-manager', () => {
+ let tmpDir: string;
+ let cacheRoot: string;
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codedelta-snap-'));
+ cacheRoot = path.join(tmpDir, '.codedelta');
+ run('git init -b main', tmpDir);
+ run('git config user.email "t@e.com"', tmpDir);
+ run('git config user.name "T"', tmpDir);
+ fs.writeFileSync(path.join(tmpDir, 'src.ts'), 'export function hello() {}\n');
+ run('git add src.ts && git commit -m "init"', tmpDir);
+ });
+
+ afterEach(() => {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('fallback extractor finds exported function', () => {
+ const snap = buildFallbackSnapshot(tmpDir, 'repo1', 'HEAD', '0.1.0');
+ expect(snap.metadata?.extractionMethod).toBe('fallback');
+ expect(snap.nodes.some((n) => n.name === 'hello')).toBe(true);
+ });
+
+ it('caches snapshot to disk', async () => {
+ const snap = buildFallbackSnapshot(tmpDir, 'repo1', 'abc123', '0.1.0');
+ await saveSnapshot(cacheRoot, snap);
+ const file = snapshotFilePath(cacheRoot, 'repo1', 'abc123', '0.1.0');
+ expect(fs.existsSync(file)).toBe(true);
+ const loaded = await loadSnapshot(cacheRoot, 'repo1', 'abc123', '0.1.0');
+ expect(loaded?.nodeCount).toBe(snap.nodeCount);
+ });
+
+ it('getOrBuildSnapshot uses worktree without mutating repo', async () => {
+ const hash = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: tmpDir, encoding: 'utf8' }).trim();
+ const snap = await getOrBuildSnapshot({
+ repoId: 'local-repo',
+ commitHash: hash,
+ clonePath: tmpDir,
+ cacheRoot,
+ analyzerVersion: 'test-0.1.0',
+ });
+ expect(snap.nodes.length).toBeGreaterThan(0);
+ expect(snap.metadata?.extractionMethod).toMatch(/codegraph|fallback/);
+ });
+});
diff --git a/packages/codedelta-snapshot-manager/package.json b/packages/codedelta-snapshot-manager/package.json
new file mode 100644
index 000000000..c460c13b8
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "@codedelta/snapshot-manager",
+ "version": "0.1.0",
+ "description": "CodeGraph snapshot builder for CodeDelta (Phase 2)",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/monorepo": "file:../../",
+ "@codedelta/repo-manager": "*",
+ "@codedelta/types": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-snapshot-manager/src/cache-paths.ts b/packages/codedelta-snapshot-manager/src/cache-paths.ts
new file mode 100644
index 000000000..8a8e9ff45
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/cache-paths.ts
@@ -0,0 +1,39 @@
+import * as fs from 'fs';
+import * as path from 'path';
+
+export function snapshotDir(
+ cacheRoot: string,
+ repoId: string,
+ commitHash: string,
+ analyzerVersion: string,
+): string {
+ return path.join(cacheRoot, 'snapshots', repoId, commitHash, analyzerVersion);
+}
+
+export function snapshotFilePath(
+ cacheRoot: string,
+ repoId: string,
+ commitHash: string,
+ analyzerVersion: string,
+): string {
+ return path.join(snapshotDir(cacheRoot, repoId, commitHash, analyzerVersion), 'snapshot.json');
+}
+
+export function readAnalyzerVersion(monorepoRoot: string): string {
+ try {
+ const pkg = JSON.parse(
+ fs.readFileSync(path.join(monorepoRoot, 'package.json'), 'utf8'),
+ ) as { version?: string };
+ return pkg.version ?? '0.0.0';
+ } catch {
+ return '0.0.0';
+ }
+}
+
+export function resolveMonorepoRoot(): string {
+ const env = process.env.CODEDELTA_MONOREPO_ROOT;
+ if (env) {
+ return path.resolve(env);
+ }
+ return path.resolve(__dirname, '../../..');
+}
diff --git a/packages/codedelta-snapshot-manager/src/codegraph-loader.ts b/packages/codedelta-snapshot-manager/src/codegraph-loader.ts
new file mode 100644
index 000000000..f8e5210b1
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/codegraph-loader.ts
@@ -0,0 +1,49 @@
+import * as path from 'path';
+import { resolveMonorepoRoot } from './cache-paths';
+
+export interface CodeGraphInstance {
+ exportGraph(): {
+ nodes: Array<{
+ id: string;
+ kind: string;
+ name: string;
+ qualifiedName: string;
+ filePath: string;
+ language: string;
+ startLine: number;
+ endLine: number;
+ signature?: string;
+ isExported?: boolean;
+ }>;
+ edges: Array<{
+ source: string;
+ target: string;
+ kind: string;
+ line?: number;
+ provenance?: string;
+ metadata?: Record;
+ }>;
+ files: string[];
+ };
+ close(): void;
+ uninitialize(): void;
+}
+
+export interface CodeGraphConstructor {
+ init(
+ projectRoot: string,
+ options?: { index?: boolean; onProgress?: (p: unknown) => void },
+ ): Promise;
+ open(projectRoot: string, options?: { sync?: boolean }): Promise;
+ isInitialized(projectRoot: string): boolean;
+ uninitialize?(projectRoot: string): void;
+}
+
+export function loadCodeGraph(): CodeGraphConstructor {
+ const root = resolveMonorepoRoot();
+ const distPath = path.join(root, 'dist', 'index.js');
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ const mod = require(distPath);
+ const CodeGraph = mod.default ?? mod.CodeGraph ?? mod;
+ return CodeGraph as CodeGraphConstructor;
+}
diff --git a/packages/codedelta-snapshot-manager/src/codegraph-snapshot.ts b/packages/codedelta-snapshot-manager/src/codegraph-snapshot.ts
new file mode 100644
index 000000000..a6edbc793
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/codegraph-snapshot.ts
@@ -0,0 +1,105 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import type { CodeGraphSnapshot, SnapshotMetadata } from '@codedelta/types';
+import { loadCodeGraph, type CodeGraphInstance } from './codegraph-loader';
+import { SnapshotBuildError } from './errors';
+import { buildIdMap, normalizeEdges, normalizeNodes, type RawEdge, type RawNode } from './semantic-id';
+
+function toRepoRelativePath(projectRoot: string, maybePath: string): string {
+ const normalized = maybePath.replace(/\\/g, '/');
+ const rootNorm = projectRoot.replace(/\\/g, '/');
+ if (normalized.startsWith(rootNorm + '/')) {
+ return normalized.slice(rootNorm.length + 1);
+ }
+ const rel = path.relative(projectRoot, maybePath).replace(/\\/g, '/');
+ if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) return rel;
+ return normalized;
+}
+
+function normalizeQualifiedName(projectRoot: string, qualifiedName: string): string {
+ const rootNorm = projectRoot.replace(/\\/g, '/');
+ return qualifiedName.replaceAll(`${rootNorm}/`, '');
+}
+
+export async function buildCodeGraphSnapshot(
+ projectRoot: string,
+ repoId: string,
+ commitHash: string,
+ analyzerVersion: string,
+): Promise {
+ const start = Date.now();
+ const CodeGraph = loadCodeGraph();
+ const resolved = path.resolve(projectRoot);
+
+ let cg: CodeGraphInstance | undefined;
+ try {
+ if (CodeGraph.isInitialized(resolved)) {
+ cg = await CodeGraph.open(resolved, { sync: false });
+ } else {
+ cg = await CodeGraph.init(resolved, { index: true });
+ }
+
+ const exported = cg.exportGraph();
+ const rawNodes: RawNode[] = exported.nodes.map((n) => ({
+ id: n.id,
+ kind: n.kind,
+ name: n.name,
+ qualifiedName: normalizeQualifiedName(resolved, n.qualifiedName),
+ filePath: toRepoRelativePath(resolved, n.filePath),
+ language: n.language,
+ startLine: n.startLine,
+ endLine: n.endLine,
+ signature: n.signature,
+ isExported: n.isExported,
+ }));
+
+ const idMap = buildIdMap(rawNodes);
+ const rawEdges: RawEdge[] = exported.edges.map((e) => ({
+ source: e.source,
+ target: e.target,
+ kind: e.kind,
+ line: e.line,
+ provenance: e.provenance,
+ metadata: e.metadata,
+ }));
+
+ const nodes = normalizeNodes(rawNodes);
+ const edges = normalizeEdges(rawEdges, idMap);
+
+ const metadata: SnapshotMetadata = {
+ extractionMethod: 'codegraph',
+ durationMs: Date.now() - start,
+ };
+
+ return {
+ repoId,
+ commitHash,
+ analyzerVersion,
+ createdAt: new Date().toISOString(),
+ nodeCount: nodes.length,
+ edgeCount: edges.length,
+ nodes,
+ edges,
+ files: exported.files.map((f) => toRepoRelativePath(resolved, f)),
+ metadata,
+ };
+ } catch (err) {
+ throw new SnapshotBuildError(
+ `CodeGraph snapshot failed: ${err instanceof Error ? err.message : String(err)}`,
+ err,
+ );
+ } finally {
+ try {
+ cg?.close();
+ } catch {
+ // ignore
+ }
+ try {
+ if (fs.existsSync(path.join(resolved, '.codegraph'))) {
+ cg?.uninitialize();
+ }
+ } catch {
+ // ignore cleanup errors
+ }
+ }
+}
diff --git a/packages/codedelta-snapshot-manager/src/errors.ts b/packages/codedelta-snapshot-manager/src/errors.ts
new file mode 100644
index 000000000..5bb5ddd33
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/errors.ts
@@ -0,0 +1,30 @@
+export class SnapshotBuildError extends Error {
+ constructor(message: string, public readonly cause?: unknown) {
+ super(message);
+ this.name = 'SnapshotBuildError';
+ }
+}
+
+export class SnapshotTooLargeError extends Error {
+ constructor(
+ message: string,
+ public readonly nodeCount: number,
+ ) {
+ super(message);
+ this.name = 'SnapshotTooLargeError';
+ }
+}
+
+export class SnapshotTimeoutError extends Error {
+ constructor(message = 'Snapshot build timed out') {
+ super(message);
+ this.name = 'SnapshotTimeoutError';
+ }
+}
+
+export class SnapshotEmptyError extends Error {
+ constructor(message = 'Repository produced an empty structural graph') {
+ super(message);
+ this.name = 'SnapshotEmptyError';
+ }
+}
diff --git a/packages/codedelta-snapshot-manager/src/fallback-extractor.ts b/packages/codedelta-snapshot-manager/src/fallback-extractor.ts
new file mode 100644
index 000000000..cf806f16f
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/fallback-extractor.ts
@@ -0,0 +1,166 @@
+/**
+ * FALLBACK extractor — minimal TS/JS structural scan when CodeGraph is unavailable.
+ * TODO: replace with full CodeGraph integration + incremental indexFiles.
+ */
+
+import * as fs from 'fs';
+import * as path from 'path';
+import type { CodeGraphSnapshot, SnapshotMetadata } from '@codedelta/types';
+import { normalizeEdges, normalizeNodes, type RawEdge, type RawNode, toSemanticId } from './semantic-id';
+
+const SOURCE_EXT = new Set(['.ts', '.tsx', '.js', '.jsx']);
+
+const EXPORT_FN = /export\s+(?:async\s+)?function\s+(\w+)/g;
+const EXPORT_CONST = /export\s+const\s+(\w+)/g;
+const EXPORT_CLASS = /export\s+class\s+(\w+)/g;
+const EXPORT_DEFAULT_FN = /export\s+default\s+function\s+(\w+)?/g;
+const IMPORT_FROM = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
+const REACT_COMPONENT = /(?:export\s+)?(?:default\s+)?function\s+([A-Z]\w*)/g;
+
+function walkDir(dir: string, files: string[] = []): string[] {
+ if (!fs.existsSync(dir)) return files;
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist') continue;
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) {
+ walkDir(full, files);
+ } else if (SOURCE_EXT.has(path.extname(entry.name))) {
+ files.push(full);
+ }
+ }
+ return files;
+}
+
+function relPath(root: string, file: string): string {
+ return path.relative(root, file).replace(/\\/g, '/');
+}
+
+function addSymbol(
+ nodes: RawNode[],
+ filePath: string,
+ kind: string,
+ name: string,
+ line: number,
+ isExported: boolean,
+): void {
+ const qualifiedName = `${filePath}::${name}`;
+ nodes.push({
+ id: qualifiedName,
+ kind,
+ name,
+ qualifiedName,
+ filePath,
+ language: filePath.endsWith('.tsx') || filePath.endsWith('.jsx') ? 'tsx' : 'typescript',
+ startLine: line,
+ endLine: line,
+ isExported,
+ });
+}
+
+function isRouteFile(filePath: string): boolean {
+ return (
+ filePath.includes('/app/') ||
+ filePath.includes('/pages/') ||
+ filePath.endsWith('/route.ts') ||
+ filePath.endsWith('/route.tsx')
+ );
+}
+
+export function buildFallbackSnapshot(
+ projectRoot: string,
+ repoId: string,
+ commitHash: string,
+ analyzerVersion: string,
+): CodeGraphSnapshot {
+ const start = Date.now();
+ const nodes: RawNode[] = [];
+ const rawEdges: RawEdge[] = [];
+ const absFiles = walkDir(projectRoot);
+ const files = absFiles.map((f) => relPath(projectRoot, f));
+
+ for (const abs of absFiles) {
+ const filePath = relPath(projectRoot, abs);
+ const content = fs.readFileSync(abs, 'utf8');
+ const lines = content.split('\n');
+
+ if (isRouteFile(filePath)) {
+ addSymbol(nodes, filePath, 'route', path.basename(filePath), 1, true);
+ }
+
+ lines.forEach((line, idx) => {
+ const lineNo = idx + 1;
+ let m: RegExpExecArray | null;
+
+ EXPORT_FN.lastIndex = 0;
+ while ((m = EXPORT_FN.exec(line)) !== null) {
+ addSymbol(nodes, filePath, 'function', m[1]!, lineNo, true);
+ }
+
+ EXPORT_CLASS.lastIndex = 0;
+ while ((m = EXPORT_CLASS.exec(line)) !== null) {
+ addSymbol(nodes, filePath, 'class', m[1]!, lineNo, true);
+ }
+
+ EXPORT_CONST.lastIndex = 0;
+ while ((m = EXPORT_CONST.exec(line)) !== null) {
+ addSymbol(nodes, filePath, 'constant', m[1]!, lineNo, true);
+ }
+
+ REACT_COMPONENT.lastIndex = 0;
+ while ((m = REACT_COMPONENT.exec(line)) !== null) {
+ addSymbol(nodes, filePath, 'component', m[1]!, lineNo, true);
+ }
+
+ IMPORT_FROM.lastIndex = 0;
+ while ((m = IMPORT_FROM.exec(line)) !== null) {
+ const target = m[1]!;
+ const fileNodeId = `${filePath}::module`;
+ if (!nodes.some((n) => n.qualifiedName === fileNodeId)) {
+ nodes.push({
+ id: fileNodeId,
+ kind: 'module',
+ name: path.basename(filePath),
+ qualifiedName: fileNodeId,
+ filePath,
+ language: 'typescript',
+ startLine: 1,
+ endLine: 1,
+ });
+ }
+ rawEdges.push({
+ source: fileNodeId,
+ target: `${target}::module`,
+ kind: 'imports',
+ line: lineNo,
+ provenance: 'heuristic',
+ });
+ }
+ });
+ }
+
+ const normalized = normalizeNodes(nodes);
+ const idMap = new Map();
+ for (const n of nodes) {
+ idMap.set(n.id, toSemanticId(n));
+ }
+ const edges = normalizeEdges(rawEdges, idMap);
+
+ const metadata: SnapshotMetadata = {
+ extractionMethod: 'fallback',
+ durationMs: Date.now() - start,
+ warnings: ['CodeGraph unavailable or failed; using minimal TS/JS fallback extractor'],
+ };
+
+ return {
+ repoId,
+ commitHash,
+ analyzerVersion,
+ createdAt: new Date().toISOString(),
+ nodeCount: normalized.length,
+ edgeCount: edges.length,
+ nodes: normalized,
+ edges,
+ files,
+ metadata,
+ };
+}
diff --git a/packages/codedelta-snapshot-manager/src/index.ts b/packages/codedelta-snapshot-manager/src/index.ts
new file mode 100644
index 000000000..ac616a7ed
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/index.ts
@@ -0,0 +1,192 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import {
+ createWorktree,
+ removeWorktree,
+ worktreePath,
+} from '@codedelta/repo-manager';
+import type { CodeGraphSnapshot } from '@codedelta/types';
+import { buildCodeGraphSnapshot } from './codegraph-snapshot';
+import { readAnalyzerVersion, resolveMonorepoRoot, snapshotFilePath } from './cache-paths';
+import {
+ SnapshotBuildError,
+ SnapshotEmptyError,
+ SnapshotTimeoutError,
+ SnapshotTooLargeError,
+} from './errors';
+import { buildFallbackSnapshot } from './fallback-extractor';
+
+export * from './errors';
+export { buildFallbackSnapshot } from './fallback-extractor';
+export { readAnalyzerVersion, resolveMonorepoRoot, snapshotFilePath } from './cache-paths';
+
+export interface GetOrBuildSnapshotOptions {
+ repoId: string;
+ commitHash: string;
+ clonePath: string;
+ cacheRoot: string;
+ analyzerVersion?: string;
+}
+
+function maxNodes(): number {
+ return parseInt(process.env.CODEDELTA_SNAPSHOT_MAX_NODES ?? '50000', 10);
+}
+
+function timeoutMs(): number {
+ return parseInt(process.env.CODEDELTA_SNAPSHOT_TIMEOUT_MS ?? '120000', 10);
+}
+
+function withTimeout(ms: number, fn: () => Promise): Promise {
+ return new Promise((resolve, reject) => {
+ const timer = setTimeout(() => reject(new SnapshotTimeoutError()), ms);
+ fn()
+ .then((v) => {
+ clearTimeout(timer);
+ resolve(v);
+ })
+ .catch((e) => {
+ clearTimeout(timer);
+ reject(e);
+ });
+ });
+}
+
+export async function loadSnapshot(
+ cacheRoot: string,
+ repoId: string,
+ commitHash: string,
+ analyzerVersion: string,
+): Promise {
+ const filePath = snapshotFilePath(cacheRoot, repoId, commitHash, analyzerVersion);
+ if (!fs.existsSync(filePath)) return null;
+ try {
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8')) as CodeGraphSnapshot;
+ if (raw.analyzerVersion !== analyzerVersion) return null;
+ return raw;
+ } catch {
+ return null;
+ }
+}
+
+export async function saveSnapshot(
+ cacheRoot: string,
+ snapshot: CodeGraphSnapshot,
+): Promise {
+ const filePath = snapshotFilePath(
+ cacheRoot,
+ snapshot.repoId,
+ snapshot.commitHash,
+ snapshot.analyzerVersion,
+ );
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
+ // Atomic write: never leave a half-written snapshot on crash/concurrent read.
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
+ fs.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2) + '\n', 'utf8');
+ fs.renameSync(tmpPath, filePath);
+}
+
+function assertSnapshotSize(snapshot: CodeGraphSnapshot): void {
+ const limit = maxNodes();
+ if (snapshot.nodeCount > limit) {
+ throw new SnapshotTooLargeError(
+ `Snapshot exceeds node limit (${snapshot.nodeCount} > ${limit})`,
+ snapshot.nodeCount,
+ );
+ }
+}
+
+async function buildFreshSnapshot(
+ options: GetOrBuildSnapshotOptions,
+ analyzerVersion: string,
+): Promise {
+ const { repoId, commitHash, clonePath, cacheRoot } = options;
+ const wtDir = worktreePath({ cacheRoot, repoId }, commitHash);
+
+ createWorktree(clonePath, commitHash, wtDir);
+
+ try {
+ let snapshot: CodeGraphSnapshot;
+
+ try {
+ snapshot = await withTimeout(timeoutMs(), () =>
+ buildCodeGraphSnapshot(wtDir, repoId, commitHash, analyzerVersion),
+ );
+ } catch (err) {
+ if (err instanceof SnapshotTimeoutError) throw err;
+ snapshot = buildFallbackSnapshot(wtDir, repoId, commitHash, analyzerVersion);
+ if (err instanceof SnapshotBuildError) {
+ snapshot.metadata = {
+ extractionMethod: 'fallback',
+ durationMs: snapshot.metadata?.durationMs,
+ warnings: [
+ ...(snapshot.metadata?.warnings ?? []),
+ `CodeGraph failed: ${err.message}`,
+ ],
+ };
+ }
+ }
+
+ if (snapshot.nodeCount === 0 && snapshot.files.length === 0) {
+ throw new SnapshotEmptyError();
+ }
+
+ assertSnapshotSize(snapshot);
+ return snapshot;
+ } finally {
+ removeWorktree(clonePath, wtDir);
+ }
+}
+
+/**
+ * Singleflight: concurrent requests for the same (repo, commit, analyzer) share
+ * one build instead of racing on the same worktree path and re-indexing twice.
+ */
+const inflightBuilds = new Map>();
+
+async function buildAndSaveSnapshot(
+ options: GetOrBuildSnapshotOptions,
+ analyzerVersion: string,
+): Promise {
+ try {
+ const snapshot = await buildFreshSnapshot(options, analyzerVersion);
+ await saveSnapshot(options.cacheRoot, snapshot);
+ return snapshot;
+ } catch (err) {
+ // Warm-up race guard: first compare can transiently fail on cold parser/runtime init.
+ // Retry once before surfacing an error.
+ if (err instanceof SnapshotEmptyError || err instanceof SnapshotTimeoutError) {
+ const snapshot = await buildFreshSnapshot(options, analyzerVersion);
+ await saveSnapshot(options.cacheRoot, snapshot);
+ return snapshot;
+ }
+ throw err;
+ }
+}
+
+/**
+ * Load cached snapshot or build via worktree + CodeGraph (fallback to TS/JS scan).
+ */
+export async function getOrBuildSnapshot(
+ options: GetOrBuildSnapshotOptions,
+): Promise {
+ const analyzerVersion =
+ options.analyzerVersion ?? readAnalyzerVersion(resolveMonorepoRoot());
+
+ const cached = await loadSnapshot(
+ options.cacheRoot,
+ options.repoId,
+ options.commitHash,
+ analyzerVersion,
+ );
+ if (cached) return cached;
+
+ const key = `${options.repoId}\u0000${options.commitHash}\u0000${analyzerVersion}`;
+ const inflight = inflightBuilds.get(key);
+ if (inflight) return inflight;
+
+ const promise = buildAndSaveSnapshot(options, analyzerVersion).finally(() => {
+ inflightBuilds.delete(key);
+ });
+ inflightBuilds.set(key, promise);
+ return promise;
+}
diff --git a/packages/codedelta-snapshot-manager/src/semantic-id.ts b/packages/codedelta-snapshot-manager/src/semantic-id.ts
new file mode 100644
index 000000000..13132426e
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/src/semantic-id.ts
@@ -0,0 +1,81 @@
+import type { CodeEdge, CodeNode } from '@codedelta/types';
+
+export interface RawNode {
+ id: string;
+ kind: string;
+ name: string;
+ qualifiedName: string;
+ filePath: string;
+ language: string;
+ startLine: number;
+ endLine: number;
+ signature?: string;
+ isExported?: boolean;
+}
+
+export interface RawEdge {
+ source: string;
+ target: string;
+ kind: string;
+ line?: number;
+ provenance?: string;
+ metadata?: Record;
+}
+
+/** Stable semantic key for diff (qualifiedName preferred). */
+export function toSemanticId(node: Pick): string {
+ if (node.qualifiedName) return node.qualifiedName;
+ return `${node.filePath}::${node.kind}::${node.name}`;
+}
+
+export function normalizeNodes(raw: RawNode[]): CodeNode[] {
+ const seen = new Map();
+ for (const n of raw) {
+ const id = toSemanticId(n);
+ if (seen.has(id)) continue;
+ seen.set(id, {
+ id,
+ kind: n.kind,
+ name: n.name,
+ qualifiedName: n.qualifiedName || id,
+ filePath: n.filePath,
+ language: n.language,
+ startLine: n.startLine,
+ endLine: n.endLine,
+ signature: n.signature,
+ isExported: n.isExported,
+ });
+ }
+ return Array.from(seen.values());
+}
+
+export function normalizeEdges(raw: RawEdge[], idMap: Map): CodeEdge[] {
+ const edges: CodeEdge[] = [];
+ const seen = new Set();
+
+ for (const e of raw) {
+ const source = idMap.get(e.source) ?? e.source;
+ const target = idMap.get(e.target) ?? e.target;
+ const key = `${source}|${target}|${e.kind}`;
+ if (seen.has(key)) continue;
+ seen.add(key);
+ edges.push({
+ source,
+ target,
+ kind: e.kind,
+ line: e.line,
+ provenance: e.provenance,
+ metadata: e.metadata,
+ });
+ }
+ return edges;
+}
+
+/** Build CodeGraph internal id → semantic id map. */
+export function buildIdMap(raw: RawNode[]): Map {
+ const map = new Map();
+ for (const n of raw) {
+ map.set(n.id, toSemanticId(n));
+ }
+ return map;
+}
diff --git a/packages/codedelta-snapshot-manager/tsconfig.json b/packages/codedelta-snapshot-manager/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-snapshot-manager/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-trace-engine/__tests__/trace-engine.test.ts b/packages/codedelta-trace-engine/__tests__/trace-engine.test.ts
new file mode 100644
index 000000000..081ddb80d
--- /dev/null
+++ b/packages/codedelta-trace-engine/__tests__/trace-engine.test.ts
@@ -0,0 +1,55 @@
+import { describe, expect, it } from 'vitest';
+import type { CommitInfo } from '@codedelta/types';
+import { extractQueryTerms, findCandidateCommits } from '../src';
+
+function commit(hash: string, message: string): CommitInfo {
+ return {
+ hash,
+ shortHash: hash.slice(0, 7),
+ message,
+ author: 't',
+ authorEmail: 't@example.com',
+ date: new Date().toISOString(),
+ parents: [],
+ changedFilesCount: 1,
+ };
+}
+
+describe('trace-engine retrieval', () => {
+ it('extracts stable query terms', () => {
+ const terms = extractQueryTerms('When did oauth login callback redirect break in auth route?');
+ expect(terms).toContain('oauth');
+ expect(terms).toContain('auth');
+ expect(terms).not.toContain('when');
+ });
+
+ it('scores matching commits higher', () => {
+ const contexts = [
+ {
+ commit: commit('a1'.padEnd(40, '1'), 'update ui styles'),
+ changedFiles: [{ path: 'src/styles.css', status: 'modified' as const }],
+ },
+ {
+ commit: commit('b2'.padEnd(40, '2'), 'fix oauth callback redirect'),
+ changedFiles: [{ path: 'src/auth/callback.ts', status: 'modified' as const }],
+ riskTags: ['auth'],
+ },
+ ];
+ const candidates = findCandidateCommits(contexts, 'oauth callback redirect auth', 2);
+ expect(candidates[0]?.commit.hash).toBe(contexts[1]?.commit.hash);
+ expect(candidates[0]?.relevanceScore).toBeGreaterThan(candidates[1]?.relevanceScore ?? 0);
+ });
+
+ it('keeps fallback candidates for weak matches', () => {
+ const contexts = [
+ {
+ commit: commit('c3'.padEnd(40, '3'), 'chore: refactor'),
+ changedFiles: [{ path: 'src/a.ts', status: 'modified' as const }],
+ },
+ ];
+ const candidates = findCandidateCommits(contexts, 'unknown issue text', 5);
+ expect(candidates).toHaveLength(1);
+ expect(candidates[0]?.reasons[0]).toContain('low direct lexical match');
+ });
+});
+
diff --git a/packages/codedelta-trace-engine/package.json b/packages/codedelta-trace-engine/package.json
new file mode 100644
index 000000000..ee5bcf18b
--- /dev/null
+++ b/packages/codedelta-trace-engine/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@codedelta/trace-engine",
+ "version": "0.1.0",
+ "description": "Issue tracing engine for CodeDelta (Phase 3)",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "test": "vitest run",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "dependencies": {
+ "@codedelta/types": "*",
+ "@codedelta/provider-runtime": "*"
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-trace-engine/src/index.ts b/packages/codedelta-trace-engine/src/index.ts
new file mode 100644
index 000000000..12a43d4df
--- /dev/null
+++ b/packages/codedelta-trace-engine/src/index.ts
@@ -0,0 +1,134 @@
+import type { ChangedFile, CommitInfo, TraceCandidateCommit } from '@codedelta/types';
+
+export interface TraceCommitContext {
+ commit: CommitInfo;
+ changedFiles: ChangedFile[];
+ riskTags?: string[];
+ changedSymbols?: string[];
+ deltaSummaryText?: string;
+ impactScore?: number;
+}
+
+const STOPWORDS = new Set([
+ 'the',
+ 'a',
+ 'an',
+ 'to',
+ 'for',
+ 'of',
+ 'and',
+ 'or',
+ 'in',
+ 'on',
+ 'is',
+ 'are',
+ 'was',
+ 'were',
+ 'be',
+ 'it',
+ 'this',
+ 'that',
+ 'with',
+ 'when',
+ 'why',
+ 'how',
+ 'what',
+ 'which',
+ 'issue',
+ 'bug',
+ 'change',
+ 'regression',
+]);
+
+function unique(arr: T[]): T[] {
+ return [...new Set(arr)];
+}
+
+function containsAny(text: string, terms: string[]): string[] {
+ const lower = text.toLowerCase();
+ return terms.filter((t) => lower.includes(t.toLowerCase()));
+}
+
+export function extractQueryTerms(question: string): string[] {
+ const terms = question
+ .toLowerCase()
+ .split(/[^a-z0-9_.\-/]+/)
+ .map((t) => t.trim())
+ .filter((t) => t.length >= 2 && !STOPWORDS.has(t));
+ return unique(terms).slice(0, 24);
+}
+
+export function scoreCommitCandidate(ctx: TraceCommitContext, terms: string[]): TraceCandidateCommit {
+ const reasons: string[] = [];
+ const matchedTerms: string[] = [];
+
+ let score = 0;
+ const msgMatches = containsAny(ctx.commit.message, terms);
+ if (msgMatches.length > 0) {
+ score += 30 + msgMatches.length * 5;
+ reasons.push(`commit message matched: ${msgMatches.join(', ')}`);
+ matchedTerms.push(...msgMatches);
+ }
+
+ const filePaths = ctx.changedFiles.map((f) => f.path);
+ const fileText = filePaths.join('\n');
+ const fileMatches = containsAny(fileText, terms);
+ if (fileMatches.length > 0) {
+ score += 25 + fileMatches.length * 4;
+ reasons.push(`changed files matched: ${fileMatches.join(', ')}`);
+ matchedTerms.push(...fileMatches);
+ }
+
+ const symbolText = (ctx.changedSymbols ?? []).join('\n');
+ const symbolMatches = containsAny(symbolText, terms);
+ if (symbolMatches.length > 0) {
+ score += 20 + symbolMatches.length * 3;
+ reasons.push(`changed symbols matched: ${symbolMatches.join(', ')}`);
+ matchedTerms.push(...symbolMatches);
+ }
+
+ const riskText = (ctx.riskTags ?? []).join('\n');
+ const riskMatches = containsAny(riskText, terms);
+ if (riskMatches.length > 0) {
+ score += 12 + riskMatches.length * 2;
+ reasons.push(`risk tags matched: ${riskMatches.join(', ')}`);
+ matchedTerms.push(...riskMatches);
+ }
+
+ if (ctx.deltaSummaryText) {
+ const deltaMatches = containsAny(ctx.deltaSummaryText, terms);
+ if (deltaMatches.length > 0) {
+ score += 8 + deltaMatches.length * 2;
+ reasons.push(`delta summary matched: ${deltaMatches.join(', ')}`);
+ matchedTerms.push(...deltaMatches);
+ }
+ }
+
+ if ((ctx.impactScore ?? 0) > 0) {
+ const boost = Math.min(8, Math.round((ctx.impactScore ?? 0) / 15));
+ score += boost;
+ reasons.push(`impact boost: +${boost}`);
+ }
+
+ // Mild recency boost: newer commits already come first from git log.
+ score += 1;
+
+ return {
+ commit: ctx.commit,
+ relevanceScore: score,
+ reasons: reasons.length > 0 ? reasons : ['low direct lexical match; included by recency fallback'],
+ matchedTerms: unique(matchedTerms),
+ changedFiles: ctx.changedFiles,
+ };
+}
+
+export function findCandidateCommits(
+ contexts: TraceCommitContext[],
+ question: string,
+ limit = 8,
+): TraceCandidateCommit[] {
+ const terms = extractQueryTerms(question);
+ const scored = contexts.map((ctx) => scoreCommitCandidate(ctx, terms));
+ const sorted = scored.sort((a, b) => b.relevanceScore - a.relevanceScore);
+ return sorted.slice(0, Math.max(1, limit));
+}
diff --git a/packages/codedelta-trace-engine/tsconfig.json b/packages/codedelta-trace-engine/tsconfig.json
new file mode 100644
index 000000000..9a82e401b
--- /dev/null
+++ b/packages/codedelta-trace-engine/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "module": "commonjs",
+ "lib": ["ES2022"],
+ "declaration": true,
+ "outDir": "./dist",
+ "rootDir": "./src",
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": true
+ },
+ "include": ["src/**/*"]
+}
diff --git a/packages/codedelta-types/package.json b/packages/codedelta-types/package.json
new file mode 100644
index 000000000..33cab3ad1
--- /dev/null
+++ b/packages/codedelta-types/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@codedelta/types",
+ "version": "0.1.0",
+ "description": "Shared TypeScript types for CodeDelta",
+ "main": "dist/index.js",
+ "types": "dist/index.d.ts",
+ "scripts": {
+ "build": "tsc",
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
+ },
+ "license": "MIT"
+}
diff --git a/packages/codedelta-types/src/index.ts b/packages/codedelta-types/src/index.ts
new file mode 100644
index 000000000..239321f55
--- /dev/null
+++ b/packages/codedelta-types/src/index.ts
@@ -0,0 +1,465 @@
+/**
+ * CodeDelta shared type definitions.
+ */
+
+/** Stable repo identifier: first 16 chars of sha256(normalizedSource). */
+export interface RepoRef {
+ id: string;
+ source: 'github' | 'local';
+ /** Original input: GitHub URL or local absolute path. */
+ input: string;
+ /** Normalized clone directory: .codedelta/repos/