diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..37b8065 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "Backend (uvicorn)", + "runtimeExecutable": "/bin/sh", + "runtimeArgs": ["-c", "PAGES_DIR=$HOME/.nomadnetwork/storage/pages SOURCES_DIR=$HOME/.micron-editor/sources /Users/dtoro/Projects/micronomicon/backend/.venv/bin/uvicorn main:app --reload --port 8080 --app-dir /Users/dtoro/Projects/micronomicon/backend"], + "port": 8080 + }, + { + "name": "Frontend (vite)", + "runtimeExecutable": "/Users/dtoro/Projects/micronomicon/frontend/node_modules/.bin/vite", + "runtimeArgs": ["/Users/dtoro/Projects/micronomicon/frontend"], + "port": 5173 + } + ] +} diff --git a/.claude/plans/nifty-beaming-hanrahan-agent-a35bf8f885e907d39.md b/.claude/plans/nifty-beaming-hanrahan-agent-a35bf8f885e907d39.md new file mode 100644 index 0000000..7c84c5e --- /dev/null +++ b/.claude/plans/nifty-beaming-hanrahan-agent-a35bf8f885e907d39.md @@ -0,0 +1,353 @@ +# uFrame Integration Plan for Micronomicon + +## Executive Summary + +Replace the existing raw-Micron editor with a uFrame IDE. uFrame is a declarative DSL that compiles `.uf` files into ASCII art and Micron `.mu` files. The user edits `.uf` source; the backend compiles it; the frontend shows dual ASCII + Micron previews. On publish, the backend compiles `.uf` to `.mu` and writes the result to PAGES_DIR. + +--- + +## Current Architecture (as-is) + +**Backend** (`backend/`): 5 Python files, no package structure. +- `main.py` — FastAPI app, mounts routers at `/api`, serves static frontend +- `pages.py` — CRUD for `.mu` files across SOURCES_DIR and PAGES_DIR +- `converter.py` — POST `/api/convert` wrapping md2txt (markdown -> micron) +- `graph.py` — GET `/api/graph` parsing Micron `[label` + `slug]` links +- `docker_utils.py` — POST `/api/restart` for NomadNet container + +**Frontend** (`frontend/src/`): +- `EditorView.tsx` — main route, split pane: `EditorPane` (CodeMirror) + `PreviewPane` (Micron HTML) +- `editorStore.ts` — state: `micronSource`, `isDirty`, `currentPage`, `previewMode` +- `pagesStore.ts` — page list, fetch/delete/unpublish +- `micronHighlight.ts` — CodeMirror StreamLanguage for Micron syntax +- `micronRenderer.ts` — Micron markup to HTML for preview +- `slashCommands.ts` — `/H1`, `/Bold`, etc. autocomplete for Micron codes +- `wikiLinkCompletion.ts` — `[[` autocomplete inserting Micron links + +**Storage model**: Sources stored as `.mu` in SOURCES_DIR, published copies in PAGES_DIR. Both are Micron markup. No intermediate format. + +**Key invariant**: NomadNet reads `.mu` files from PAGES_DIR. This must be preserved — uFrame compiles to `.mu` for publishing. + +--- + +## Phase 1 — uFrame Core Engine (Backend Python Package) + +### 1.1 Package Structure + +Create `backend/uframe/` as a Python package: + +``` +backend/uframe/ + __init__.py # Public API: compile(source, width) -> CompileResult + parser.py # Line-by-line indentation parser -> IR tree + ir.py # IR node dataclasses (Page, Box, Row, Col, Text, etc.) + grid.py # CharGrid class — 2D character + style buffer + measure.py # Bottom-up size computation pass + layout.py # Top-down position assignment pass + paint.py # Write chars into CharGrid + borders.py # Border merge pass — fix junction characters + emit_ascii.py # CharGrid -> plain text string + emit_micron.py # CharGrid -> Micron markup with style tags + errors.py # ParseError, LayoutError exception types +``` + +### 1.2 New Files to Create + +**`backend/uframe/__init__.py`** +Public API surface. Exports `compile(source: str, width: int = 72) -> CompileResult`. +CompileResult is a dataclass with fields: `ascii: str`, `micron: str`, `warnings: list[str]`. +The function orchestrates: parse -> measure -> layout -> paint -> merge_borders -> emit. + +**`backend/uframe/ir.py`** +IR node types as Python dataclasses. Every node has: +- `children: list[Node]` +- `width`, `height` (computed by measure) +- `x`, `y` (computed by layout) +- `style: Style` (fg, bg, bold, italic, underline) + +Node types for Phase 1: +- `Page` — root, has `width` property, optional `title` +- `Box` — border weight (none/light/heavy/double/rounded), padding +- `Row` — horizontal container, children laid out left-to-right +- `Col` — vertical container, children laid out top-to-bottom +- `Spacer` — flexible space filler +- `Pad` — explicit padding wrapper +- `Heading` — level 1-3, renders with Micron `>`, `>>`, `>>>` +- `Text` — body text with inline @modifiers (@bold, @italic, @color) +- `Label` — fixed-width key:value pair +- `Divider` — horizontal rule with character choice +- `Link` — generates `[label` + `target]` in Micron output +- `List` — bulleted or numbered list items + +**`backend/uframe/parser.py`** +Line-by-line, indentation-based parser. Design: +- Each line is `(indent_level, keyword, props_string)` +- Indent = 2 spaces per level (configurable) +- Keywords: page, box, row, col, spacer, pad, heading, text, label, divider, link, list +- Properties parsed from the rest of the line after the keyword +- Multiline text: subsequent indented lines without a keyword are text content +- Returns ParseTree with root Page node and warnings list +- Raises ParseError with line number + message for unrecoverable errors + +**`backend/uframe/grid.py`** +CharGrid — the core rendering buffer: +- 2D array of Cell objects: (char, fg, bg, bold, italic, underline, border_flag) +- Methods: put(x, y, char, style), put_str(x, y, text, style), hline(x, y, length, char), vline(x, y, length, char), rect(x, y, w, h, weight) +- Border characters stored with border=True for the merge pass +- width and height properties + +**`backend/uframe/measure.py`** +Bottom-up pass computing sizes: +- Leaf nodes (Text, Heading, Label, Divider) compute natural size +- Text wraps to available width, computes resulting height +- Row: width = sum(child widths), height = max(child heights) +- Col: width = max(child widths), height = sum(child heights) +- Box: adds 2 for borders (if weight != none), plus padding +- Percentage-based widths resolved relative to parent + +**`backend/uframe/layout.py`** +Top-down pass assigning positions: +- Page starts at (0, 0) +- Row distributes x positions left-to-right +- Col distributes y positions top-to-bottom +- Spacer expands to fill remaining space + +**`backend/uframe/paint.py`** +Renders each node into the CharGrid: +- Box: draws border rectangle, then paints children inside +- Text: writes word-wrapped text with style +- Heading: writes text, marks heading level for Micron emitter +- Divider: draws horizontal line +- Label: writes "key: value" with key highlighted +- Link: writes label text, stores target in metadata for Micron emitter + +**`backend/uframe/borders.py`** +Post-paint pass over CharGrid: +- Scans for adjacent border cells +- Replaces with correct junction characters (T, cross, corner) +- Unicode box-drawing character lookup tables +- Handles mixed weights (light meets heavy, etc.) + +**`backend/uframe/emit_ascii.py`** +Reads CharGrid row by row, outputs plain characters only. Strips trailing whitespace per line. + +**`backend/uframe/emit_micron.py`** +Reads CharGrid row by row, inserts Micron formatting codes: +- Track current style state, emit format codes on change +- Headings get `>`, `>>`, `>>>` prefixes +- Links emit Micron link syntax +- Reset formatting at end of styled runs + +**`backend/uframe/errors.py`** +Exception types: UFrameError (base), ParseError, LayoutError. Each carries line/col/message. + +### 1.3 Existing Files to Modify + +**`backend/converter.py`** — REPLACE contents. +Remove md2txt wrapper. Add uFrame compile endpoint: +- POST `/api/compile` accepts `{source: str, width: int}`, returns `{ascii: str, micron: str, warnings: list[str]}` +- Old POST `/api/convert` can remain as deprecated during transition + +**`backend/pages.py`** — Modify storage model. +- SOURCES_DIR stores `.uf` files instead of `.mu` files +- `_list_all_page_names()`: scan for `.uf` in SOURCES_DIR, `.mu` in PAGES_DIR +- `get_page()`: returns `.uf` source from SOURCES_DIR +- `save_page()`: accepts `.uf` source, saves as `.uf` +- On publish: compile .uf -> .mu via uframe.compile(), write .mu to PAGES_DIR +- `_extract_title()`: parse .uf source for heading or page title= property +- PageDetail model: add `source` field (the .uf content) +- SaveRequest model: `source` field replaces `micron` + +**`backend/graph.py`** — Modify link extraction. +- Parse .uf source files for link nodes instead of Micron link pattern +- Use uframe.parser.parse() to get IR tree, walk for Link nodes +- Or: simpler regex for link keyword in .uf syntax +- `_extract_title()`: parse .uf for page title= or first heading + +**`backend/requirements.txt`** — Remove md2txt dependency (eventually). + +### 1.4 API Changes + +| Method | Path | Before | After | +|--------|------|--------|-------| +| POST | `/api/convert` | markdown -> micron | DEPRECATED | +| POST | `/api/compile` | NEW | .uf source -> {ascii, micron, warnings} | +| GET | `/api/pages/{name}` | returns {name, micron} | returns {name, source, micron} | +| POST | `/api/pages/{name}` | body: {micron, publish} | body: {source, publish} | + +### 1.5 Migration Strategy for Existing Pages + +Existing .mu source files in SOURCES_DIR need a migration path: +- Option A (recommended): One-time migration script `backend/migrate_mu_to_uf.py` wrapping existing Micron in minimal .uf template +- Option B: Keep .mu fallback — if .uf not found but .mu exists in SOURCES_DIR, return raw Micron as read-only legacy +- pages.py get_page should check for .uf first, fall back to .mu + +### 1.6 Verification Steps + +1. **Unit tests** — `backend/tests/test_uframe/`: + - test_parser.py, test_grid.py, test_measure.py, test_layout.py + - test_borders.py, test_emit_ascii.py, test_emit_micron.py, test_compile.py + +2. **Golden file tests** — `backend/tests/test_uframe/golden/`: + - .uf input files paired with expected .ascii and .mu outputs + - Cases: single box, nested rows/cols, heading+text page, mixed content + +3. **API test** — verify POST /api/compile returns valid response +4. **Edge cases**: empty page, deeply nested boxes, text overflow, zero-width + +--- + +## Phase 2 — Data Visualization Nodes + +### 2.1 New Files + +**`backend/uframe/viz.py`** — Viz node implementations: +- Gauge, Meter, BarH, BarV, Sparkline (braille), Heatmap, Status, Table + +### 2.2 Modified Files + +- `backend/uframe/ir.py` — add viz node dataclasses +- `backend/uframe/parser.py` — add viz keyword handlers +- `backend/uframe/measure.py` — add viz size computation +- `backend/uframe/paint.py` — add viz rendering + +### 2.3 Verification + +- Golden file tests for each viz type +- Edge cases: empty data, single value, overflow + +--- + +## Phase 3 — Web IDE (Frontend Replacement) + +### 3.1 New Files + +**`frontend/src/components/editor/uframeHighlight.ts`** +CodeMirror language support for .uf syntax: +- Keywords: page, box, row, col, spacer, pad, heading, text, label, divider, link, list, gauge, sparkline, etc. +- Properties: width=, height=, weight=, title=, char=, @modifiers +- String literals, comments (#), indentation awareness + +**`frontend/src/components/editor/uframeCommands.ts`** +Slash commands for .uf syntax: +- /box, /row, /col, /heading, /text, /divider, etc. +- Each inserts a snippet with correct indentation +- Property suggestions after = + +**`frontend/src/components/editor/AsciiPreviewPane.tsx`** +ASCII output preview: +- Monospace rendering of plain-text ASCII art +- Terminal aesthetic (green-on-black optional) + +**`frontend/src/hooks/useCompile.ts`** +Hook calling POST /api/compile: +- Debounced (300ms) on source change +- Returns { ascii, micron, warnings, isCompiling, error } + +### 3.2 Modified Files + +**`frontend/src/stores/editorStore.ts`** — Major rewrite: +- `ufSource` replaces `micronSource` +- Add `compiledAscii`, `compiledMicron`, `warnings`, `isCompiling` +- `previewMode`: "ascii" | "micron" | "raw" (was "preview" | "raw") + +**`frontend/src/routes/EditorView.tsx`** — Significant changes: +- Use ufSource instead of micronSource +- Add compile-on-change hook (debounced /api/compile) +- Save sends source field not micron +- Replace micronHighlight with uframeHighlight +- Replace slashCommands with uframeCommands +- Replace single PreviewPane with tabbed ASCII + Micron preview + +**`frontend/src/components/editor/PreviewPane.tsx`** — Major rewrite: +- Tab bar: "ASCII" | "Micron" | "Raw Micron" +- ASCII tab: compiledAscii in monospace pre block +- Micron tab: compiledMicron through existing renderMicron() +- Raw tab: raw Micron source + +**`frontend/src/components/editor/ToolBar.tsx`** — Minor: +- Compile warnings count in status area + +**`frontend/src/stores/pagesStore.ts`** — Minor: +- unpublishPage sends source not micron + +**`frontend/src/components/editor/wikiLinkCompletion.ts`** — Adapt to .uf link syntax + +### 3.3 Removed Files + +- `frontend/src/components/editor/slashCommands.ts` (replaced by uframeCommands.ts) +- `frontend/src/components/editor/micronHighlight.ts` (replaced by uframeHighlight.ts) + +### 3.4 Files Kept As-Is + +- `micronRenderer.ts` — still needed for Micron preview tab +- `oneDarkTheme.ts` — theme remains +- `DashboardView.tsx`, `GraphView.tsx` — no changes +- All shadcn/ui components — unchanged + +### 3.5 Verification + +- Create page with boxes, headings, text — verify ASCII and Micron previews +- Publish writes correct .mu to PAGES_DIR +- Page load round-trip: save .uf, reload, content preserved +- Dashboard still shows pages correctly +- Graph still shows links +- Keyboard shortcuts (Ctrl+S, Ctrl+P) still work +- Unsaved changes guard still fires + +--- + +## Phase 4+ — Forms, Dynamic, Components (Future) + +### Forms (Phase 4) +- IR nodes: Field, Password, Radio, Checkbox, Button +- Parser + measure + paint for each +- Micron emit generates form codes + +### Dynamic (Phase 5) +- source blocks, on_submit, if/else, for loops, state +- Requires runtime component beyond static compilation + +### Components (Phase 6) +- Reusable definitions, import, parameterized props + +--- + +## Implementation Order (Phase 1 detail) + +Build sequence within Phase 1, each step testable in isolation: + +1. `errors.py` — Exception types (no dependencies) +2. `ir.py` — Node dataclasses (depends on nothing) +3. `parser.py` — Parse .uf -> IR tree (depends on ir, errors) +4. `grid.py` — CharGrid buffer (depends on nothing) +5. `measure.py` — Size computation (depends on ir) +6. `layout.py` — Position assignment (depends on ir) +7. `paint.py` — Render to grid (depends on ir, grid) +8. `borders.py` — Junction resolution (depends on grid) +9. `emit_ascii.py` — ASCII output (depends on grid) +10. `emit_micron.py` — Micron output (depends on grid) +11. `__init__.py` — Public API tying it together +12. `converter.py` update — Wire /api/compile endpoint +13. `pages.py` update — .uf storage model +14. `graph.py` update — .uf link extraction + +--- + +## Key Design Decisions + +1. **Separate measure/layout/paint passes** — enables caching (re-layout without re-parse) and independent testability. + +2. **CharGrid as intermediate** — decouples layout from emission. Same grid produces ASCII and Micron. Adding new emitters (HTML, ANSI) later is trivial. + +3. **Border flag on cells** — merge pass identifies borders without confusing them with box-drawing text content. + +4. **Style state in cells** — each cell carries its own style so Micron emitter can diff adjacent cells and emit minimal formatting codes. + +5. **Line-based parser** — matches indentation-sensitive DSL. Simpler than full tokenizer. Upgradeable to PEG grammar later. + +6. **No new Python dependencies** — pure stdlib. Box-drawing and braille characters are Unicode literals. + +7. **SOURCES_DIR uses .uf extension** — clean break from .mu sources. Published files remain .mu (NomadNet requirement). + +8. **Compile on backend** — single source of truth. Frontend is thin editor + preview client. Enables future CLI compilation and batch processing. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..5a5c5df --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,290 @@ +# Micronomicon → µFrame + +A self-hosted web IDE for building rich terminal UIs that publish as `.mu` pages to a NomadNet node. + +> **Evolution:** Started as a raw Micron text editor (Phases 1–2, complete). +> Now pivoting to **µFrame** — a declarative DSL that compiles `.uf` files into +> both plain ASCII art and styled Micron `.mu` output from the same source. + +## Status + +Phases 1–2 complete (raw Micron editor). Phase 3 (µFrame engine) is next. + +## Tech Stack + +| Layer | Technology | +|------------|--------------------------------------------------| +| Backend | Python 3.13 + FastAPI + uvicorn | +| µFrame | Pure Python: parser → IR → CharGrid → emitters | +| Frontend | React 19 + Vite + TypeScript | +| UI | shadcn/ui + Tailwind CSS v4 + tw-animate-css | +| Editor | CodeMirror 6 | +| Graph | React Flow (@xyflow/react) + dagre | +| State | Zustand | +| Container | Docker + Compose | + +## Directory Layout + +``` +micronomicon/ + Dockerfile + compose.yml + docs/ + framework-design-v3.md # µFrame DSL spec + rendering model + dynamic-templates.md # Dynamic page addendum (Phases 5–6) + backend/ + main.py # FastAPI app + static file serving + converter.py # POST /api/compile (µFrame → ASCII + Micron) + pages.py # file management (CRUD /api/pages) + graph.py # link parser (GET /api/graph) + docker_utils.py # container restart (POST /api/restart) + requirements.txt + uframe/ # µFrame engine (Phase 3+) + __init__.py # compile(source, width) → CompileResult + errors.py # ParseError, LayoutError + ir.py # IR node dataclasses + parser.py # .uf DSL → IR tree + grid.py # CharGrid (2D char + style buffer) + chars.py # Unicode lookup tables (box-drawing, braille) + measure.py # bottom-up size computation + layout.py # top-down position assignment + paint.py # IR → CharGrid rendering + borders.py # junction merging post-pass + emit_ascii.py # CharGrid → plain text + emit_micron.py # CharGrid → Micron with style tags + viz.py # gauge, sparkline, status (Phase 4) + table.py # table layout + box-drawn grid (Phase 4) + frontend/ + src/ + App.tsx + routes/ # DashboardView, EditorView, GraphView + components/ + dashboard/ # page list, status badges + editor/ + EditorPane.tsx # CodeMirror host + PreviewPane.tsx # ASCII + Micron + Raw preview tabs + ToolBar.tsx # save/publish + backlinks + uframeHighlight.ts # CM6 .uf syntax highlighting (Phase 3+) + uframeCommands.ts # "/" command palette for .uf (Phase 3+) + micronRenderer.ts # Micron → HTML (renders compiled output) + BacklinkIndicator.tsx + shared/ + ui/ # shadcn components + stores/ # editorStore, pagesStore (Zustand) + hooks/ # useCompile, useGraph, useUnsavedGuard + lib/ # utils (cn) +``` + +## Local Development + +**Backend:** +```bash +cd backend +source .venv/bin/activate +PAGES_DIR=~/.nomadnetwork/storage/pages \ +SOURCES_DIR=~/.micron-editor/sources \ +uvicorn main:app --reload --port 8080 +``` + +**Frontend:** +```bash +cd frontend +npm run dev # proxies /api -> localhost:8080 +``` + +Frontend at http://localhost:5173, backend at http://localhost:8080. + +## API Endpoints + +| Method | Path | Description | +|--------|---------------------|------------------------------------------------------| +| GET | /api/health | Health check | +| POST | /api/compile | Compile `.uf` → `{ascii, micron, warnings}` | +| GET | /api/pages | List all pages with metadata | +| GET | /api/pages/{name} | Read page source (`.uf` or legacy `.mu`) | +| POST | /api/pages/{name} | Save page — body `{ source, publish: bool }` | +| DELETE | /api/pages/{name} | Delete source and/or .mu file | +| GET | /api/graph | Graph nodes + edges (parsed from links in source) | +| POST | /api/restart | Restart NomadNet Docker container | + +## Storage + +``` +~/.micron-editor/sources/ ← .uf source files (draft + published) +~/.nomadnetwork/storage/pages/ ← Compiled .mu files served by NomadNet +``` + +On publish: `.uf` is compiled to `.mu` and copied to the NomadNet pages directory. + +## Conventions + +- Frontend UI components live in `frontend/src/components/ui/` (shadcn) +- Feature components grouped by domain: `dashboard/`, `editor/`, `shared/` +- State management via Zustand stores in `frontend/src/stores/` +- Backend is pure FastAPI; no ORM, flat file storage +- µFrame engine is pure Python stdlib — no external dependencies + +## µFrame DSL Reference + +Full spec: `docs/framework-design-v3.md` + +### Layout primitives +``` +page "Title" [width] # root container (default 64) + box [light|heavy|double|rounded] "Title" # bordered panel + row [gap] # horizontal layout + col [width] # column in a row + spacer [lines] # vertical whitespace + pad [t] [r] [b] [l] # inner margin +``` + +### Content primitives +``` +heading [1|2|3] "Text" # styled heading +text "Content with @bold{inline} @color{hex}{modifiers}" +label "Key" "Value" # aligned key-value pair +list [bullet|dash] + item "Entry" +link "Display text" "/dest.mu" # clickable in Micron +divider [light|heavy|double|dash|dot] # horizontal rule +# comment # ignored in output +``` + +### Data visualization (Phase 4) +``` +gauge "Label" $val $max $width warn=N crit=N +sparkline "Label" $values $width # braille patterns +status "Label" [online|offline|degraded] # ●○◐ indicators +table "Title" + columns "Name" 24 | "Hops" 6 | "Status" 10 + row "value" | "value" | "value" +``` + +### Forms (Phase 5) +``` +form "name" + field "name" [width] "placeholder" + radio "group" "Opt A" | "Opt B" + checkbox "name" "Label" + button "Label" "/action/path" +``` + +### Dynamic features (Phase 6) +``` +source cpu : shell "cat /proc/loadavg" +on_submit "form_name" + # handle form data +if $val > threshold + # conditional rendering +for item in $collection + # iteration +state "store" "/path.json" +cache 0 # never cache (re-execute per request) +``` + +### Rendering pipeline +``` +.uf source → Parse → IR Tree → Measure → Layout → Paint → CharGrid + ├→ ASCII emitter (plain text) + └→ Micron emitter (styled .mu) +``` + +### Micron syntax (legacy raw editor, still used for compiled output) +``` +>H1 >>H2 >>>H3 headings +`!bold`! `*italic`* `_underline`_ formatting +`Fhex text`f `Bhex text`b colors (3-digit hex) +`c text`a `r text`a `l text`a alignment +[label`slug] links +-─ -━ -═ -★ dividers +`= ... `= literal mode +# comment hidden in output +``` + +## Roadmap + +### Phase 1 — Core Editor + Dashboard + Graph ✅ + +- Split-pane Micron editor with live preview +- Save draft / publish to NomadNet pages dir +- Pages dashboard with status badges +- Page graph (React Flow + dagre) +- NomadNet restart, dark/light theme, keyboard shortcuts + +### Phase 2 — Linking + Editor Enhancements ✅ + +- `[[` page link autocomplete +- Backlink indicator with popover +- Pivot to direct Micron editor (removed Markdown pipeline) +- `/` slash command palette for Micron syntax +- Micron syntax highlighting + HTML preview renderer +- Syntax completeness per micron-composer spec + +### Phase 3 — µFrame Core Engine (next) + +Build `backend/uframe/` — the rendering pipeline: + +1. `errors.py` + `ir.py` — data structures +2. `chars.py` — Unicode lookup tables (box-drawing, blocks, braille) +3. `parser.py` — `.uf` DSL → IR tree (indentation-based, line-oriented) +4. `grid.py` — CharGrid class (2D char + style buffer) +5. `measure.py` + `layout.py` — size computation + position assignment +6. `paint.py` — IR nodes → CharGrid +7. `borders.py` — junction merging post-pass +8. `emit_ascii.py` + `emit_micron.py` — CharGrid → output strings +9. `__init__.py` — public `compile()` API +10. Update `converter.py` → `POST /api/compile` endpoint +11. Update `pages.py` — `.uf` sources, compile-on-publish +12. Update `graph.py` — parse `.uf` for links + +### Phase 4 — Data Visualization + +- gauge, meter, bar_h, bar_v (block elements) +- sparkline (braille sub-cell rendering) +- heatmap (shade blocks with per-cell color) +- status indicators (●○◐ with color) +- table (box-drawn with header separator) +- Border merging across nested tables + +### Phase 5 — Web IDE Integration + +- Replace Micron editor with µFrame DSL editor +- CodeMirror `.uf` syntax highlighting + autocomplete +- `useCompile` hook (debounced API calls) +- Triple preview: ASCII | Micron rendered | Raw Micron +- Page storage: `.uf` sources, `.mu` compiled output +- Migration script for legacy `.mu` sources + +### Phase 6 — Forms & Interactivity + +- Form primitives: field, password, radio, checkbox, button +- ASCII: visual placeholders; Micron: live fields +- @modifier inline syntax +- Variables and `let` bindings + +### Phase 7 — Dynamic Pages + +- `source` blocks for live data (shell, file, json, python, rns) +- Compile to executable Python scripts with embedded runtime +- `on_submit` form handling via `FIELD_*` env vars +- Conditionals (`if`/`elif`/`else`) and loops (`for`) +- State persistence (`state` + JSON store) +- Cache control headers +- CLI: `uframe compile` / `uframe deploy` + +### Phase 8 — Components & Standard Library + +- `component` definitions with argument bindings +- Standard library: `std/dashboard`, `std/filebrowser`, `std/board` +- Themes (`.uf-theme` color palette files) +- `uframe check` linter +- Production deployment: systemd + Tailscale + +## References + +- µFrame design: `docs/framework-design-v3.md` +- µFrame dynamic: `docs/dynamic-templates.md` +- micron-composer: https://github.com/fr33n0w/micron-composer +- micron-parser-js: https://rfnexus.github.io/micron-parser-js/ +- NomadNet: https://github.com/markqvist/NomadNet +- md2txt (legacy): https://codeberg.org/randogoth/md2txt diff --git a/backend/converter.py b/backend/converter.py index 628c438..d0b2965 100644 --- a/backend/converter.py +++ b/backend/converter.py @@ -1,28 +1,34 @@ +"""µFrame compile endpoint — POST /api/compile.""" + from fastapi import APIRouter, HTTPException from pydantic import BaseModel +import uframe +from uframe.errors import UFrameError + router = APIRouter() -class ConvertRequest(BaseModel): - markdown: str - width: int = 80 +class CompileRequest(BaseModel): + source: str + width: int = 64 -class ConvertResponse(BaseModel): +class CompileResponse(BaseModel): + ascii: str micron: str + warnings: list[str] -@router.post("/convert", response_model=ConvertResponse) -async def convert(req: ConvertRequest): +@router.post("/compile", response_model=CompileResponse) +async def compile_source(req: CompileRequest): + """Compile µFrame .uf source into ASCII and Micron output.""" try: - from md2txt import convert_markdown - - result = convert_markdown( - req.markdown, - width=req.width, - renderer_name="micron", + result = uframe.compile(req.source, width=req.width) + return CompileResponse( + ascii=result.ascii, + micron=result.micron, + warnings=[w.message for w in result.warnings], ) - return ConvertResponse(micron=result) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Conversion failed: {e}") + except UFrameError as e: + raise HTTPException(status_code=422, detail=str(e)) diff --git a/backend/graph.py b/backend/graph.py index 7584f31..91829d3 100644 --- a/backend/graph.py +++ b/backend/graph.py @@ -10,8 +10,8 @@ router = APIRouter() SOURCES_DIR = Path(os.environ.get("SOURCES_DIR", "/data/sources")) PAGES_DIR = Path(os.environ.get("PAGES_DIR", "/data/pages")) -# Matches markdown links: [text](slug) where slug has no protocol or path separators -_INTERNAL_LINK = re.compile(r"\[([^\]]+)\]\(([a-zA-Z0-9_-]+)\)") +# Matches Micron links: [label`slug] or [label`slug.mu] +_INTERNAL_LINK = re.compile(r'\[([^`\]]+)`([a-zA-Z0-9_-]+)(?:\.mu)?\]') class GraphNode(BaseModel): @@ -38,16 +38,16 @@ def _all_page_names() -> set[str]: names.add(f.stem) if SOURCES_DIR.is_dir(): for f in SOURCES_DIR.iterdir(): - if f.suffix == ".md" and f.is_file(): + if f.suffix == ".mu" and f.is_file(): names.add(f.stem) return names -def _extract_title(markdown: str) -> str | None: - for line in markdown.splitlines(): +def _extract_title(micron: str) -> str | None: + for line in micron.splitlines(): stripped = line.strip() - if stripped.startswith("# "): - return stripped[2:].strip() + if stripped.startswith(">") and not stripped.startswith(">>"): + return stripped[1:].strip() return None @@ -58,15 +58,14 @@ async def get_graph(): edges: list[GraphEdge] = [] for name in sorted(all_names): - md_path = SOURCES_DIR / f"{name}.md" + src_path = SOURCES_DIR / f"{name}.mu" mu_path = PAGES_DIR / f"{name}.mu" title = None - if md_path.is_file(): - content = md_path.read_text(encoding="utf-8") + if src_path.is_file(): + content = src_path.read_text(encoding="utf-8") title = _extract_title(content) - # Parse internal links for match in _INTERNAL_LINK.finditer(content): target = match.group(2) if target in all_names: diff --git a/backend/main.py b/backend/main.py index fe5101c..21c2deb 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,12 +4,12 @@ from pathlib import Path from fastapi import FastAPI from fastapi.staticfiles import StaticFiles -from converter import router as converter_router from pages import router as pages_router from graph import router as graph_router from docker_utils import router as docker_router +from converter import router as converter_router -app = FastAPI(title="Micron Page Editor") +app = FastAPI(title="µFrame Editor") app.include_router(converter_router, prefix="/api") app.include_router(pages_router, prefix="/api") diff --git a/backend/pages.py b/backend/pages.py index b9aeb3c..7201392 100644 --- a/backend/pages.py +++ b/backend/pages.py @@ -1,9 +1,12 @@ import os +import shlex from pathlib import Path from fastapi import APIRouter, HTTPException from pydantic import BaseModel +import uframe + router = APIRouter() PAGES_DIR = Path(os.environ.get("PAGES_DIR", "/data/pages")) @@ -21,20 +24,42 @@ class PageMeta(BaseModel): class PageDetail(BaseModel): name: str - markdown: str | None = None - micron: str | None = None + source: str | None = None class SaveRequest(BaseModel): - markdown: str + source: str publish: bool = False -def _extract_title(markdown: str) -> str | None: - for line in markdown.splitlines(): +def _extract_title(source: str) -> str | None: + """Extract title from µFrame source or legacy Micron.""" + for line in source.splitlines(): stripped = line.strip() - if stripped.startswith("# "): - return stripped[2:].strip() + if not stripped or stripped.startswith("#"): + continue + # µFrame: page "Title" [width] + if stripped.lower().startswith("page "): + try: + parts = shlex.split(stripped) + if len(parts) >= 2: + return parts[1] + except ValueError: + pass + break + # µFrame: heading 1 "Title" + if stripped.lower().startswith("heading "): + try: + parts = shlex.split(stripped) + if len(parts) >= 3: + return parts[2] + except ValueError: + pass + break + # Legacy Micron: >Title + if stripped.startswith(">") and not stripped.startswith(">>"): + return stripped[1:].strip() + break return None @@ -46,18 +71,29 @@ def _list_all_page_names() -> set[str]: names.add(f.stem) if SOURCES_DIR.is_dir(): for f in SOURCES_DIR.iterdir(): - if f.suffix == ".md" and f.is_file(): + if f.suffix in (".uf", ".mu") and f.is_file(): names.add(f.stem) return names +def _source_path(name: str) -> Path: + """Get source file path, preferring .uf over legacy .mu.""" + uf = SOURCES_DIR / f"{name}.uf" + if uf.is_file(): + return uf + mu = SOURCES_DIR / f"{name}.mu" + return mu if mu.is_file() else uf # default to .uf for new files + + def _page_meta(name: str) -> PageMeta: + src_path = _source_path(name) mu_path = PAGES_DIR / f"{name}.mu" - md_path = SOURCES_DIR / f"{name}.md" title = None - if md_path.is_file(): - title = _extract_title(md_path.read_text(encoding="utf-8")) + if src_path.is_file(): + title = _extract_title(src_path.read_text(encoding="utf-8")) + elif mu_path.is_file(): + title = _extract_title(mu_path.read_text(encoding="utf-8")) published = mu_path.is_file() last_modified = mu_path.stat().st_mtime if published else None @@ -67,7 +103,7 @@ def _page_meta(name: str) -> PageMeta: name=name, title=title, published=published, - has_source=md_path.is_file(), + has_source=src_path.is_file(), last_modified=last_modified, size=size, ) @@ -80,16 +116,19 @@ async def list_pages(): @router.get("/pages/{name}", response_model=PageDetail) async def get_page(name: str): - md_path = SOURCES_DIR / f"{name}.md" + src_path = _source_path(name) mu_path = PAGES_DIR / f"{name}.mu" - if not md_path.is_file() and not mu_path.is_file(): + if not src_path.is_file() and not mu_path.is_file(): raise HTTPException(status_code=404, detail="Page not found") - markdown = md_path.read_text(encoding="utf-8") if md_path.is_file() else None - micron = mu_path.read_text(encoding="utf-8") if mu_path.is_file() else None + source = ( + src_path.read_text(encoding="utf-8") + if src_path.is_file() + else mu_path.read_text(encoding="utf-8") + ) - return PageDetail(name=name, markdown=markdown, micron=micron) + return PageDetail(name=name, source=source) @router.post("/pages/{name}", response_model=PageMeta) @@ -97,39 +136,40 @@ async def save_page(name: str, req: SaveRequest): SOURCES_DIR.mkdir(parents=True, exist_ok=True) PAGES_DIR.mkdir(parents=True, exist_ok=True) - # Always save markdown source - md_path = SOURCES_DIR / f"{name}.md" - md_path.write_text(req.markdown, encoding="utf-8") + # Save source as .uf + src_path = SOURCES_DIR / f"{name}.uf" + src_path.write_text(req.source, encoding="utf-8") - # Optionally publish + # Remove legacy .mu source if it exists + legacy_mu = SOURCES_DIR / f"{name}.mu" + if legacy_mu.is_file(): + legacy_mu.unlink() + + # Publish: compile .uf → .mu and write to pages dir if req.publish: try: - from md2txt import convert_markdown - - micron = convert_markdown( - req.markdown, - width=80, - renderer_name="micron", - ) + result = uframe.compile(req.source) + mu_path = PAGES_DIR / f"{name}.mu" + mu_path.write_text(result.micron, encoding="utf-8") except Exception as e: - raise HTTPException(status_code=500, detail=f"Conversion failed: {e}") - - mu_path = PAGES_DIR / f"{name}.mu" - mu_path.write_text(micron, encoding="utf-8") + raise HTTPException( + status_code=422, + detail=f"Compile failed during publish: {e}", + ) return _page_meta(name) @router.delete("/pages/{name}") async def delete_page(name: str): - md_path = SOURCES_DIR / f"{name}.md" + src_path = _source_path(name) mu_path = PAGES_DIR / f"{name}.mu" - if not md_path.is_file() and not mu_path.is_file(): + if not src_path.is_file() and not mu_path.is_file(): raise HTTPException(status_code=404, detail="Page not found") - if md_path.is_file(): - md_path.unlink() + if src_path.is_file(): + src_path.unlink() if mu_path.is_file(): mu_path.unlink() diff --git a/backend/uframe/__init__.py b/backend/uframe/__init__.py new file mode 100644 index 0000000..768c8e3 --- /dev/null +++ b/backend/uframe/__init__.py @@ -0,0 +1,74 @@ +"""µFrame — A DSL for rich terminal UIs rendered as ASCII and Micron. + +Public API: + compile(source, width=64) → CompileResult +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from uframe.errors import CompileWarning, UFrameError +from uframe.parser import parse +from uframe.measure import measure +from uframe.layout import layout +from uframe.paint import paint +from uframe.borders import merge_borders +from uframe.grid import CharGrid +from uframe.emit_ascii import emit_ascii +from uframe.emit_micron import emit_micron + + +@dataclass +class CompileResult: + """Result of compiling a .uf source.""" + ascii: str = "" + micron: str = "" + warnings: list[CompileWarning] = field(default_factory=list) + + +def compile(source: str, width: int = 64) -> CompileResult: + """Compile a µFrame .uf source string into ASCII and Micron output. + + Args: + source: the .uf DSL source text + width: page width in characters (default 64) + + Returns: + CompileResult with ascii, micron, and any warnings + + Raises: + ParseError: if the source cannot be parsed + LayoutError: if layout constraints fail + """ + warnings: list[CompileWarning] = [] + + # 1. Parse + page = parse(source) + if page.width == 64 and width != 64: + page.width = width + + w = page.width + + # 2. Measure + measure(page, w) + + # 3. Layout + total_h = layout(page, 0, 0, w, page.pref_height + 100) + + # 4. Create grid and paint + grid = CharGrid(w, max(total_h, 1)) + paint(page, grid) + + # 5. Merge borders + merge_borders(grid) + + # 6. Emit + ascii_out = emit_ascii(grid) + micron_out = emit_micron(grid, page_title=page.title) + + return CompileResult( + ascii=ascii_out, + micron=micron_out, + warnings=warnings, + ) diff --git a/backend/uframe/borders.py b/backend/uframe/borders.py new file mode 100644 index 0000000..a906eae --- /dev/null +++ b/backend/uframe/borders.py @@ -0,0 +1,148 @@ +"""Border merging post-pass — fix junction characters where borders meet. + +Scans the CharGrid for adjacent border cells and replaces with the +correct junction character (T-junctions, crosses, corners) from the +Unicode box-drawing set. +""" + +from __future__ import annotations + +from uframe.grid import CharGrid +from uframe.ir import BorderWeight + +# --------------------------------------------------------------------------- +# Connection detection +# --------------------------------------------------------------------------- + +# For each border cell, check which directions have adjacent borders. +# Direction flags: +UP = 1 +DOWN = 2 +LEFT = 4 +RIGHT = 8 + +# Junction lookup: connections bitmask → character +# Only light weight for now (most common case) +_LIGHT_JUNCTIONS: dict[int, str] = { + UP | DOWN: "│", + LEFT | RIGHT: "─", + DOWN | RIGHT: "┌", + DOWN | LEFT: "┐", + UP | RIGHT: "└", + UP | LEFT: "┘", + UP | DOWN | RIGHT: "├", + UP | DOWN | LEFT: "┤", + LEFT | RIGHT | DOWN: "┬", + LEFT | RIGHT | UP: "┴", + UP | DOWN | LEFT | RIGHT: "┼", + RIGHT: "─", + LEFT: "─", + UP: "│", + DOWN: "│", +} + +_HEAVY_JUNCTIONS: dict[int, str] = { + UP | DOWN: "┃", + LEFT | RIGHT: "━", + DOWN | RIGHT: "┏", + DOWN | LEFT: "┓", + UP | RIGHT: "┗", + UP | LEFT: "┛", + UP | DOWN | RIGHT: "┣", + UP | DOWN | LEFT: "┫", + LEFT | RIGHT | DOWN: "┳", + LEFT | RIGHT | UP: "┻", + UP | DOWN | LEFT | RIGHT: "╋", + RIGHT: "━", + LEFT: "━", + UP: "┃", + DOWN: "┃", +} + +_DOUBLE_JUNCTIONS: dict[int, str] = { + UP | DOWN: "║", + LEFT | RIGHT: "═", + DOWN | RIGHT: "╔", + DOWN | LEFT: "╗", + UP | RIGHT: "╚", + UP | LEFT: "╝", + UP | DOWN | RIGHT: "╠", + UP | DOWN | LEFT: "╣", + LEFT | RIGHT | DOWN: "╦", + LEFT | RIGHT | UP: "╩", + UP | DOWN | LEFT | RIGHT: "╬", + RIGHT: "═", + LEFT: "═", + UP: "║", + DOWN: "║", +} + +_JUNCTION_TABLES = { + BorderWeight.LIGHT: _LIGHT_JUNCTIONS, + BorderWeight.HEAVY: _HEAVY_JUNCTIONS, + BorderWeight.DOUBLE: _DOUBLE_JUNCTIONS, + BorderWeight.ROUNDED: _LIGHT_JUNCTIONS, # rounded uses light junctions +} + +# Weight priority for mixed-weight junctions +_WEIGHT_PRIORITY = { + BorderWeight.DOUBLE: 3, + BorderWeight.HEAVY: 2, + BorderWeight.LIGHT: 1, + BorderWeight.ROUNDED: 0, +} + + +def merge_borders(grid: CharGrid) -> None: + """Scan the grid for adjacent border cells and fix junction characters. + + This pass resolves cases where two boxes share an edge or corner, + replacing the overlapping border characters with proper junctions. + """ + for row in range(grid.height): + for col in range(grid.width): + cell = grid.cells[row][col] + if not cell.is_border: + continue + + # Detect connections in 4 directions + connections = 0 + max_weight = cell.border_weight or BorderWeight.LIGHT + + # Check each neighbor + if row > 0 and grid.cells[row - 1][col].is_border: + connections |= UP + nw = grid.cells[row - 1][col].border_weight or BorderWeight.LIGHT + if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0): + max_weight = nw + + if row < grid.height - 1 and grid.cells[row + 1][col].is_border: + connections |= DOWN + nw = grid.cells[row + 1][col].border_weight or BorderWeight.LIGHT + if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0): + max_weight = nw + + if col > 0 and grid.cells[row][col - 1].is_border: + connections |= LEFT + nw = grid.cells[row][col - 1].border_weight or BorderWeight.LIGHT + if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0): + max_weight = nw + + if col < grid.width - 1 and grid.cells[row][col + 1].is_border: + connections |= RIGHT + nw = grid.cells[row][col + 1].border_weight or BorderWeight.LIGHT + if _WEIGHT_PRIORITY.get(nw, 0) > _WEIGHT_PRIORITY.get(max_weight, 0): + max_weight = nw + + # Skip rounded corners — they should preserve ╭╮╰╯ + if cell.border_weight == BorderWeight.ROUNDED and connections in ( + DOWN | RIGHT, DOWN | LEFT, UP | RIGHT, UP | LEFT + ): + continue + + # Look up the junction character + if connections: + table = _JUNCTION_TABLES.get(max_weight, _LIGHT_JUNCTIONS) + junction = table.get(connections) + if junction: + cell.char = junction diff --git a/backend/uframe/chars.py b/backend/uframe/chars.py new file mode 100644 index 0000000..b71a3f7 --- /dev/null +++ b/backend/uframe/chars.py @@ -0,0 +1,158 @@ +"""Unicode character lookup tables for box-drawing, block elements, braille, and indicators.""" + +from __future__ import annotations + +from uframe.ir import BorderWeight + +# --------------------------------------------------------------------------- +# Box-drawing characters by weight +# --------------------------------------------------------------------------- + +# Keys: (weight) → dict of part names → char +BOX_CHARS: dict[BorderWeight, dict[str, str]] = { + BorderWeight.LIGHT: { + "tl": "┌", "tr": "┐", "bl": "└", "br": "┘", + "h": "─", "v": "│", + "t_down": "┬", "t_up": "┴", "t_right": "├", "t_left": "┤", + "cross": "┼", + }, + BorderWeight.HEAVY: { + "tl": "┏", "tr": "┓", "bl": "┗", "br": "┛", + "h": "━", "v": "┃", + "t_down": "┳", "t_up": "┻", "t_right": "┣", "t_left": "┫", + "cross": "╋", + }, + BorderWeight.DOUBLE: { + "tl": "╔", "tr": "╗", "bl": "╚", "br": "╝", + "h": "═", "v": "║", + "t_down": "╦", "t_up": "╩", "t_right": "╠", "t_left": "╣", + "cross": "╬", + }, + BorderWeight.ROUNDED: { + "tl": "╭", "tr": "╮", "bl": "╰", "br": "╯", + "h": "─", "v": "│", + "t_down": "┬", "t_up": "┴", "t_right": "├", "t_left": "┤", + "cross": "┼", + }, +} + +# --------------------------------------------------------------------------- +# Block elements for gauges and bars +# --------------------------------------------------------------------------- + +# Horizontal fill blocks: full → 1/8 +HFILL = "█▉▊▋▌▍▎▏" + +# Vertical fill blocks: 1/8 → full (bottom-up) +VFILL = "▁▂▃▄▅▆▇█" + +# Shade blocks: 25% → 100% +SHADE = "░▒▓█" + +# Gauge characters +GAUGE_FILLED = "█" +GAUGE_EMPTY = "░" + +# --------------------------------------------------------------------------- +# Braille patterns for sparklines +# --------------------------------------------------------------------------- + +# Braille base: U+2800. Each character is a 2×4 dot matrix. +# Dot positions (bit index): +# 0 3 +# 1 4 +# 2 5 +# 6 7 +BRAILLE_BASE = 0x2800 + +# Row dot bits for left column (bits 0,1,2,6) and right column (bits 3,4,5,7) +BRAILLE_LEFT = [0x01, 0x02, 0x04, 0x40] # rows 0-3 +BRAILLE_RIGHT = [0x08, 0x10, 0x20, 0x80] # rows 0-3 + + +def braille_char(dots: list[tuple[int, int]]) -> str: + """Build a braille character from a list of (col, row) positions. + + col: 0 (left) or 1 (right) + row: 0 (top) to 3 (bottom) + """ + code = BRAILLE_BASE + for col, row in dots: + if 0 <= row <= 3: + if col == 0: + code |= BRAILLE_LEFT[row] + else: + code |= BRAILLE_RIGHT[row] + return chr(code) + + +def sparkline_chars(values: list[float], width: int) -> list[str]: + """Convert a list of values into braille sparkline characters. + + Each output character represents two consecutive values (left + right columns). + Values are normalized to 0–7 (mapping to 4 braille rows × 2 resolution). + """ + if not values: + return [] + + lo = min(values) + hi = max(values) + span = hi - lo if hi != lo else 1.0 + + # Normalize to 0–7 range (8 vertical positions: 4 rows × 2 resolution) + norm = [int((v - lo) / span * 7) for v in values] + + # Pad to even length + if len(norm) % 2: + norm.append(norm[-1]) + + chars = [] + for i in range(0, min(len(norm), width * 2), 2): + left_val = norm[i] + right_val = norm[i + 1] if i + 1 < len(norm) else norm[i] + + dots = [] + # Fill dots from bottom up for each column + for row in range(3, -1, -1): + threshold = (3 - row) * 2 # row 3=0, row 2=2, row 1=4, row 0=6 + if left_val >= threshold: + dots.append((0, row)) + if right_val >= threshold: + dots.append((1, row)) + + chars.append(braille_char(dots)) + + return chars[:width] + + +# --------------------------------------------------------------------------- +# Status indicators +# --------------------------------------------------------------------------- + +STATUS_CHARS: dict[str, str] = { + "online": "●", + "offline": "○", + "degraded": "◐", + "unknown": "◌", + "alert": "⚠", +} + +STATUS_COLORS: dict[str, str] = { + "online": "0f0", + "offline": "f00", + "degraded": "ff0", + "unknown": "888", + "alert": "f00", +} + +# --------------------------------------------------------------------------- +# Divider characters +# --------------------------------------------------------------------------- + +DIVIDER_CHARS: dict[str, str] = { + "light": "─", + "heavy": "━", + "double": "═", + "dash": "╌", + "dot": "┄", +} diff --git a/backend/uframe/emit_ascii.py b/backend/uframe/emit_ascii.py new file mode 100644 index 0000000..e23638d --- /dev/null +++ b/backend/uframe/emit_ascii.py @@ -0,0 +1,22 @@ +"""ASCII emitter — read CharGrid and output plain text. + +Reads only cell.char from each cell. No color, no style tags. +""" + +from __future__ import annotations + +from uframe.grid import CharGrid + + +def emit_ascii(grid: CharGrid) -> str: + """Emit the CharGrid as plain ASCII text.""" + lines: list[str] = [] + for row in range(grid.height): + line = "".join(cell.char for cell in grid.cells[row]).rstrip() + lines.append(line) + + # Strip trailing blank lines + while lines and not lines[-1]: + lines.pop() + + return "\n".join(lines) diff --git a/backend/uframe/emit_micron.py b/backend/uframe/emit_micron.py new file mode 100644 index 0000000..022f753 --- /dev/null +++ b/backend/uframe/emit_micron.py @@ -0,0 +1,121 @@ +"""Micron emitter — read CharGrid and output Micron markup with style tags. + +Scans each line left-to-right, tracks style state, and opens/closes +Micron format codes at style transitions. Box-drawing characters pass +through as literal text. + +Micron format reference: + `!bold`! `*italic`* `_underline`_ + `Fhex text`f `Bhex text`b + `c center`a `r right`a + [label`dest] [label`dest.mu] + >H1 >>H2 >>>H3 +""" + +from __future__ import annotations + +from uframe.grid import CharGrid, CellStyle +from uframe.ir import Page, Heading, HeadingLevel + + +def _emit_style_open(style: CellStyle) -> str: + """Emit Micron opening tags for a style.""" + tags: list[str] = [] + if style.bold: + tags.append("`!") + if style.italic: + tags.append("`*") + if style.underline: + tags.append("`_") + if style.fg: + tags.append(f"`F{style.fg}") + if style.bg: + tags.append(f"`B{style.bg}") + return "".join(tags) + + +def _emit_style_close(style: CellStyle) -> str: + """Emit Micron closing tags for a style (in reverse order).""" + tags: list[str] = [] + if style.bg: + tags.append("`b") + if style.fg: + tags.append("`f") + if style.underline: + tags.append("`_") + if style.italic: + tags.append("`*") + if style.bold: + tags.append("`!") + return "".join(tags) + + +_EMPTY_STYLE = CellStyle() + + +def emit_micron(grid: CharGrid, page_title: str = "") -> str: + """Emit the CharGrid as Micron markup. + + Args: + grid: the rendered character grid + page_title: optional page title for a leading >Title line + + Returns: + Micron source string + """ + lines: list[str] = [] + + for row in range(grid.height): + line_parts: list[str] = [] + cur_style = _EMPTY_STYLE + in_link: str | None = None + + for col in range(grid.width): + cell = grid.cells[row][col] + ch = cell.char + style = cell.style + link = cell.link + + # Handle link transitions + if link != in_link: + if in_link is not None: + # Close previous link: [label`dest] + line_parts.append(f"`{in_link}]") + if link is not None: + # Close any open style before link + if cur_style != _EMPTY_STYLE: + line_parts.append(_emit_style_close(cur_style)) + cur_style = _EMPTY_STYLE + # Open new link + line_parts.append("[") + in_link = link + + # Handle style transitions (not inside links — links handle their own style) + if link is None and style != cur_style: + # Close previous style + if cur_style != _EMPTY_STYLE: + line_parts.append(_emit_style_close(cur_style)) + # Open new style + if style != _EMPTY_STYLE: + line_parts.append(_emit_style_open(style)) + cur_style = style + + line_parts.append(ch) + + # Close any trailing link: [label`dest] + if in_link is not None: + line_parts.append(f"`{in_link}]") + in_link = None + + # Close any trailing style + if cur_style != _EMPTY_STYLE: + line_parts.append(_emit_style_close(cur_style)) + + line = "".join(line_parts).rstrip() + lines.append(line) + + # Strip trailing blank lines + while lines and not lines[-1]: + lines.pop() + + return "\n".join(lines) diff --git a/backend/uframe/errors.py b/backend/uframe/errors.py new file mode 100644 index 0000000..ffc1ce6 --- /dev/null +++ b/backend/uframe/errors.py @@ -0,0 +1,45 @@ +"""µFrame error types with source location tracking.""" + +from __future__ import annotations + + +class UFrameError(Exception): + """Base error for all µFrame operations.""" + + def __init__(self, message: str, line: int | None = None, col: int | None = None): + self.line = line + self.col = col + loc = "" + if line is not None: + loc = f" (line {line}" + if col is not None: + loc += f", col {col}" + loc += ")" + super().__init__(f"{message}{loc}") + + +class ParseError(UFrameError): + """Raised when .uf source cannot be parsed.""" + pass + + +class LayoutError(UFrameError): + """Raised when layout constraints cannot be satisfied.""" + pass + + +class CompileWarning: + """Non-fatal issue discovered during compilation.""" + + __slots__ = ("message", "line", "col") + + def __init__(self, message: str, line: int | None = None, col: int | None = None): + self.message = message + self.line = line + self.col = col + + def __repr__(self) -> str: + loc = "" + if self.line is not None: + loc = f" line={self.line}" + return f"CompileWarning({self.message!r}{loc})" diff --git a/backend/uframe/grid.py b/backend/uframe/grid.py new file mode 100644 index 0000000..fabf176 --- /dev/null +++ b/backend/uframe/grid.py @@ -0,0 +1,170 @@ +"""CharGrid — 2D character buffer with per-cell style annotations. + +The CharGrid is the intermediate representation between layout and emission. +Both the ASCII and Micron emitters read from the same grid. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from uframe.chars import BOX_CHARS +from uframe.ir import BorderWeight + + +@dataclass +class CellStyle: + """Per-cell visual style for Micron emission.""" + fg: str | None = None # 3-digit hex color + bg: str | None = None + bold: bool = False + italic: bool = False + underline: bool = False + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CellStyle): + return NotImplemented + return (self.fg == other.fg and self.bg == other.bg + and self.bold == other.bold and self.italic == other.italic + and self.underline == other.underline) + + def __hash__(self) -> int: + return hash((self.fg, self.bg, self.bold, self.italic, self.underline)) + + +@dataclass +class Cell: + """A single cell in the CharGrid.""" + char: str = " " + style: CellStyle = field(default_factory=CellStyle) + is_border: bool = False # True for box-drawing characters (for merge pass) + border_weight: BorderWeight | None = None + link: str | None = None # Micron link destination + + +class CharGrid: + """2D buffer of cells. Origin (0,0) is top-left.""" + + __slots__ = ("width", "height", "cells") + + def __init__(self, width: int, height: int): + self.width = width + self.height = height + self.cells: list[list[Cell]] = [ + [Cell() for _ in range(width)] + for _ in range(height) + ] + + def in_bounds(self, x: int, y: int) -> bool: + return 0 <= x < self.width and 0 <= y < self.height + + def put(self, x: int, y: int, char: str, + style: CellStyle | None = None, + is_border: bool = False, + border_weight: BorderWeight | None = None, + link: str | None = None) -> None: + """Write a single character to the grid.""" + if not self.in_bounds(x, y): + return + cell = self.cells[y][x] + cell.char = char + if style is not None: + cell.style = style + cell.is_border = is_border + cell.border_weight = border_weight + if link is not None: + cell.link = link + + def put_text(self, x: int, y: int, text: str, + style: CellStyle | None = None, + link: str | None = None) -> int: + """Write a string horizontally starting at (x, y). + + Returns the number of characters actually written. + """ + written = 0 + for i, ch in enumerate(text): + px = x + i + if not self.in_bounds(px, y): + break + self.put(px, y, ch, style=style, link=link) + written += 1 + return written + + def fill_rect(self, x: int, y: int, w: int, h: int, char: str = " ", + style: CellStyle | None = None) -> None: + """Fill a rectangular region with a character.""" + for row in range(y, y + h): + for col in range(x, x + w): + self.put(col, row, char, style=style) + + def draw_border(self, x: int, y: int, w: int, h: int, + weight: BorderWeight = BorderWeight.LIGHT, + title: str = "", + title_style: CellStyle | None = None) -> None: + """Draw a box border. Interior is not cleared. + + Args: + x, y: top-left corner + w, h: outer dimensions (including border) + weight: border style + title: optional title inset in top border + title_style: style for the title text + """ + if w < 2 or h < 2: + return + + ch = BOX_CHARS[weight] + border_style = CellStyle() + + # Corners + self.put(x, y, ch["tl"], border_style, is_border=True, border_weight=weight) + self.put(x + w - 1, y, ch["tr"], border_style, is_border=True, border_weight=weight) + self.put(x, y + h - 1, ch["bl"], border_style, is_border=True, border_weight=weight) + self.put(x + w - 1, y + h - 1, ch["br"], border_style, is_border=True, border_weight=weight) + + # Top and bottom edges + for col in range(x + 1, x + w - 1): + self.put(col, y, ch["h"], border_style, is_border=True, border_weight=weight) + self.put(col, y + h - 1, ch["h"], border_style, is_border=True, border_weight=weight) + + # Left and right edges + for row in range(y + 1, y + h - 1): + self.put(x, row, ch["v"], border_style, is_border=True, border_weight=weight) + self.put(x + w - 1, row, ch["v"], border_style, is_border=True, border_weight=weight) + + # Title in top border + if title and w > 4: + title_text = f" {title} " + max_title = w - 4 # leave room for corners + padding + if len(title_text) > max_title: + title_text = title_text[:max_title] + + start_x = x + 2 + ts = title_style or CellStyle(bold=True) + self.put_text(start_x, y, title_text, style=ts) + + def grow_height(self, new_height: int) -> None: + """Expand the grid vertically if needed.""" + if new_height <= self.height: + return + for _ in range(new_height - self.height): + self.cells.append([Cell() for _ in range(self.width)]) + self.height = new_height + + def get_line(self, row: int) -> str: + """Get a single row as a plain string (chars only).""" + if 0 <= row < self.height: + return "".join(cell.char for cell in self.cells[row]) + return "" + + def to_text(self) -> str: + """Emit the entire grid as plain text (ASCII mode).""" + lines = [] + for row in range(self.height): + line = self.get_line(row).rstrip() + lines.append(line) + # Strip trailing blank lines + while lines and not lines[-1]: + lines.pop() + return "\n".join(lines) diff --git a/backend/uframe/ir.py b/backend/uframe/ir.py new file mode 100644 index 0000000..a448e3e --- /dev/null +++ b/backend/uframe/ir.py @@ -0,0 +1,243 @@ +"""µFrame Intermediate Representation — node types for the IR tree. + +Every .uf source parses into a tree of IRNode subclasses. The layout +engine measures, positions, and paints these nodes into a CharGrid. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum, auto +from typing import Any + + +# --------------------------------------------------------------------------- +# Enums +# --------------------------------------------------------------------------- + +class BorderWeight(Enum): + LIGHT = auto() + HEAVY = auto() + DOUBLE = auto() + ROUNDED = auto() + + +class HeadingLevel(Enum): + H1 = 1 + H2 = 2 + H3 = 3 + + +class DividerStyle(Enum): + LIGHT = auto() + HEAVY = auto() + DOUBLE = auto() + DASH = auto() + DOT = auto() + + +class ListStyle(Enum): + BULLET = auto() + DASH = auto() + NUMBER = auto() + ARROW = auto() + + +class Align(Enum): + LEFT = auto() + CENTER = auto() + RIGHT = auto() + + +# --------------------------------------------------------------------------- +# Style +# --------------------------------------------------------------------------- + +@dataclass +class Style: + """Visual style attached to any node.""" + fg: str | None = None # 3-digit hex + bg: str | None = None # 3-digit hex + bold: bool = False + italic: bool = False + underline: bool = False + align: Align = Align.LEFT + + +# --------------------------------------------------------------------------- +# Inline text spans (parsed from @modifier{} syntax in text content) +# --------------------------------------------------------------------------- + +@dataclass +class TextSpan: + """A run of text with optional inline styling.""" + text: str + bold: bool = False + italic: bool = False + underline: bool = False + fg: str | None = None + bg: str | None = None + + +# --------------------------------------------------------------------------- +# Layout rect — assigned by the layout engine +# --------------------------------------------------------------------------- + +@dataclass +class Rect: + x: int = 0 + y: int = 0 + w: int = 0 + h: int = 0 + + +# --------------------------------------------------------------------------- +# Base node +# --------------------------------------------------------------------------- + +@dataclass +class IRNode: + """Base class for all IR nodes.""" + children: list[IRNode] = field(default_factory=list) + style: Style = field(default_factory=Style) + rect: Rect = field(default_factory=Rect) + source_line: int | None = None + + # Set by measure pass + min_width: int = 0 + min_height: int = 0 + pref_width: int = 0 + pref_height: int = 0 + + +# --------------------------------------------------------------------------- +# Layout nodes +# --------------------------------------------------------------------------- + +@dataclass +class Page(IRNode): + """Root container. One per .uf file.""" + title: str = "" + width: int = 64 + + +@dataclass +class Box(IRNode): + """Bordered panel with optional title.""" + title: str = "" + weight: BorderWeight = BorderWeight.LIGHT + + +@dataclass +class Row(IRNode): + """Horizontal layout — children split available width.""" + gap: int = 1 + + +@dataclass +class Col(IRNode): + """Explicit column in a row. Width in chars or None (auto).""" + col_width: int | None = None + + +@dataclass +class Spacer(IRNode): + """Vertical whitespace.""" + lines: int = 1 + + +@dataclass +class Pad(IRNode): + """Inner margin for a container.""" + top: int = 0 + right: int = 0 + bottom: int = 0 + left: int = 0 + + +# --------------------------------------------------------------------------- +# Content nodes +# --------------------------------------------------------------------------- + +@dataclass +class Heading(IRNode): + """Styled heading (levels 1–3).""" + level: HeadingLevel = HeadingLevel.H1 + text: str = "" + + +@dataclass +class Text(IRNode): + """Text content with optional @modifier{} inline styles.""" + content: str = "" + spans: list[TextSpan] = field(default_factory=list) + + +@dataclass +class Label(IRNode): + """Aligned key-value pair.""" + key: str = "" + value: str = "" + + +@dataclass +class Divider(IRNode): + """Full-width horizontal rule.""" + divider_style: DividerStyle = DividerStyle.LIGHT + + +@dataclass +class Link(IRNode): + """Clickable link — visual in ASCII, interactive in Micron.""" + display: str = "" + dest: str = "" + + +@dataclass +class ListNode(IRNode): + """Bulleted or numbered list.""" + list_style: ListStyle = ListStyle.BULLET + + +@dataclass +class ListItem(IRNode): + """Single entry in a ListNode.""" + content: str = "" + + +# --------------------------------------------------------------------------- +# Placeholder nodes for future phases +# --------------------------------------------------------------------------- + +@dataclass +class Gauge(IRNode): + """Horizontal bar chart (Phase 4).""" + label: str = "" + value: float = 0 + max_val: float = 100 + bar_width: int = 28 + warn: float | None = None + crit: float | None = None + + +@dataclass +class Sparkline(IRNode): + """Braille sparkline (Phase 4).""" + label: str = "" + values: list[float] = field(default_factory=list) + spark_width: int = 20 + + +@dataclass +class Status(IRNode): + """Status indicator (Phase 4).""" + label: str = "" + state: str = "unknown" + + +@dataclass +class Table(IRNode): + """Box-drawn table (Phase 4).""" + title: str = "" + columns: list[tuple[str, int]] = field(default_factory=list) # (name, width) + rows: list[list[str]] = field(default_factory=list) diff --git a/backend/uframe/layout.py b/backend/uframe/layout.py new file mode 100644 index 0000000..ee908f7 --- /dev/null +++ b/backend/uframe/layout.py @@ -0,0 +1,134 @@ +"""Top-down layout pass — assign (x, y, w, h) positions to every node.""" + +from __future__ import annotations + +from uframe.ir import ( + IRNode, Page, Box, Row, Col, Spacer, Pad, Rect, + Heading, Text, Label, Divider, Link, ListNode, ListItem, + Gauge, Sparkline, Status, +) + + +def layout(node: IRNode, x: int, y: int, w: int, h: int) -> int: + """Assign positions to a node and its children. + + Args: + node: the IR node to lay out + x, y: top-left position in the grid + w: available width + h: available height (advisory, may grow) + + Returns: + The actual height consumed by this node. + """ + node.rect = Rect(x=x, y=y, w=w, h=0) + + if isinstance(node, Page): + cursor_y = y + for child in node.children: + child_h = layout(child, x, cursor_y, w, h - (cursor_y - y)) + cursor_y += child_h + node.rect.h = cursor_y - y + return node.rect.h + + elif isinstance(node, Box): + # Border takes 1 char on each side + inner_x = x + 1 + inner_y = y + 1 + inner_w = w - 2 + cursor_y = inner_y + for child in node.children: + child_h = layout(child, inner_x, cursor_y, inner_w, + h - 2 - (cursor_y - inner_y)) + cursor_y += child_h + inner_h = cursor_y - inner_y + node.rect.h = inner_h + 2 # +2 for top/bottom border + return node.rect.h + + elif isinstance(node, Row): + n = len(node.children) + if n == 0: + return 0 + + gap_total = node.gap * (n - 1) + usable = w - gap_total + + # Distribute width + widths: list[int] = [] + fixed_total = 0 + flex_count = 0 + for child in node.children: + if isinstance(child, Col) and child.col_width is not None: + widths.append(child.col_width) + fixed_total += child.col_width + else: + widths.append(0) + flex_count += 1 + + flex_each = max(1, (usable - fixed_total) // flex_count) if flex_count > 0 else 0 + remainder = (usable - fixed_total) - (flex_each * flex_count) if flex_count > 0 else 0 + + for i, child in enumerate(node.children): + if widths[i] == 0: + widths[i] = flex_each + if remainder > 0: + widths[i] += 1 + remainder -= 1 + + # Lay out each child at its column position + max_h = 0 + col_x = x + for i, child in enumerate(node.children): + child_h = layout(child, col_x, y, widths[i], h) + max_h = max(max_h, child_h) + col_x += widths[i] + node.gap + + node.rect.h = max_h + return max_h + + elif isinstance(node, Col): + cursor_y = y + col_w = node.col_width if node.col_width is not None else w + col_w = min(col_w, w) + for child in node.children: + child_h = layout(child, x, cursor_y, col_w, h - (cursor_y - y)) + cursor_y += child_h + node.rect.w = col_w + node.rect.h = cursor_y - y + return node.rect.h + + elif isinstance(node, Spacer): + node.rect.h = node.lines + return node.lines + + elif isinstance(node, Pad): + cursor_y = y + node.top + inner_w = w - node.left - node.right + for child in node.children: + child_h = layout(child, x + node.left, cursor_y, inner_w, + h - node.top - node.bottom - (cursor_y - y - node.top)) + cursor_y += child_h + node.rect.h = (cursor_y - y) + node.bottom + return node.rect.h + + elif isinstance(node, (Heading, Text, Label, Divider, Link, ListItem, + Gauge, Sparkline, Status)): + node.rect.h = node.pref_height + return node.pref_height + + elif isinstance(node, ListNode): + cursor_y = y + for child in node.children: + child_h = layout(child, x + 2, cursor_y, w - 2, h - (cursor_y - y)) + cursor_y += child_h + node.rect.h = cursor_y - y + return node.rect.h + + else: + # Generic vertical stacking + cursor_y = y + for child in node.children: + child_h = layout(child, x, cursor_y, w, h - (cursor_y - y)) + cursor_y += child_h + node.rect.h = cursor_y - y + return node.rect.h diff --git a/backend/uframe/measure.py b/backend/uframe/measure.py new file mode 100644 index 0000000..354be71 --- /dev/null +++ b/backend/uframe/measure.py @@ -0,0 +1,206 @@ +"""Bottom-up measure pass — compute min/preferred width and height for each node.""" + +from __future__ import annotations + +from uframe.ir import ( + IRNode, Page, Box, Row, Col, Spacer, Pad, + Heading, Text, Label, Divider, Link, ListNode, ListItem, + Gauge, Sparkline, Status, +) + + +def _text_height(text: str, width: int) -> int: + """Compute how many lines a text string needs at a given width.""" + if not text or width <= 0: + return 1 + words = text.split() + lines = 1 + col = 0 + for word in words: + wlen = len(word) + if col > 0 and col + 1 + wlen > width: + lines += 1 + col = wlen + elif col == 0: + col = wlen + else: + col += 1 + wlen + return max(lines, 1) + + +def measure(node: IRNode, available_width: int) -> None: + """Recursively compute min/preferred sizes for an IR subtree. + + This is a bottom-up pass: children are measured before their parent. + """ + # Dispatch to node type + if isinstance(node, Page): + w = node.width + node.pref_width = w + node.min_width = w + # Measure children with full page width + total_h = 0 + for child in node.children: + measure(child, w) + total_h += child.pref_height + node.pref_height = total_h + node.min_height = total_h + + elif isinstance(node, Box): + # Box adds 2 chars for borders on each axis (left+right, top+bottom) + inner_w = available_width - 2 + total_h = 0 + for child in node.children: + measure(child, inner_w) + total_h += child.pref_height + node.pref_width = available_width + node.min_width = 4 # minimum: border + 2 chars + border + node.pref_height = total_h + 2 # +2 for top/bottom border + node.min_height = 3 # top border + 1 line + bottom border + + elif isinstance(node, Row): + # Children laid out horizontally, split available width + n = len(node.children) + if n == 0: + node.pref_width = available_width + node.pref_height = 0 + node.min_width = 0 + node.min_height = 0 + return + + gap_total = node.gap * (n - 1) + usable = available_width - gap_total + + # First pass: measure children to get their preferred sizes + # Distribute width proportionally or equally + fixed_width_children = [] + flex_children = [] + fixed_total = 0 + + for child in node.children: + if isinstance(child, Col) and child.col_width is not None: + fixed_width_children.append(child) + fixed_total += child.col_width + else: + flex_children.append(child) + + flex_each = 0 + if flex_children: + flex_each = max(1, (usable - fixed_total) // len(flex_children)) + + max_h = 0 + for child in node.children: + if isinstance(child, Col) and child.col_width is not None: + child_w = child.col_width + else: + child_w = flex_each + measure(child, child_w) + max_h = max(max_h, child.pref_height) + + node.pref_width = available_width + node.min_width = n # at minimum 1 char per child + node.pref_height = max_h + node.min_height = max_h + + elif isinstance(node, Col): + w = node.col_width if node.col_width is not None else available_width + total_h = 0 + for child in node.children: + measure(child, w) + total_h += child.pref_height + node.pref_width = w + node.min_width = min(w, 1) + node.pref_height = total_h + node.min_height = total_h + + elif isinstance(node, Spacer): + node.pref_width = available_width + node.min_width = 0 + node.pref_height = node.lines + node.min_height = node.lines + + elif isinstance(node, Pad): + inner_w = available_width - node.left - node.right + total_h = 0 + for child in node.children: + measure(child, inner_w) + total_h += child.pref_height + node.pref_width = available_width + node.min_width = node.left + node.right + 1 + node.pref_height = total_h + node.top + node.bottom + node.min_height = node.top + node.bottom + + elif isinstance(node, Heading): + node.pref_width = available_width + node.min_width = len(node.text) + 1 + node.pref_height = 1 + node.min_height = 1 + + elif isinstance(node, Text): + node.pref_width = available_width + node.min_width = 1 + node.pref_height = _text_height(node.content, available_width) + node.min_height = 1 + + elif isinstance(node, Label): + node.pref_width = available_width + node.min_width = len(node.key) + 2 + len(node.value) + node.pref_height = 1 + node.min_height = 1 + + elif isinstance(node, Divider): + node.pref_width = available_width + node.min_width = 1 + node.pref_height = 1 + node.min_height = 1 + + elif isinstance(node, Link): + node.pref_width = available_width + node.min_width = len(node.display) + 2 + node.pref_height = 1 + node.min_height = 1 + + elif isinstance(node, ListNode): + total_h = 0 + for child in node.children: + measure(child, available_width - 2) # indent for bullet + total_h += child.pref_height + node.pref_width = available_width + node.min_width = 4 + node.pref_height = total_h + node.min_height = total_h + + elif isinstance(node, ListItem): + node.pref_width = available_width + node.min_width = len(node.content) + 1 + node.pref_height = _text_height(node.content, available_width) + node.min_height = 1 + + elif isinstance(node, Gauge): + node.pref_width = available_width + node.min_width = node.bar_width + len(node.label) + 6 + node.pref_height = 1 + node.min_height = 1 + + elif isinstance(node, Sparkline): + node.pref_width = available_width + node.min_width = node.spark_width + len(node.label) + 4 + node.pref_height = 1 + node.min_height = 1 + + elif isinstance(node, Status): + node.pref_width = available_width + node.min_width = len(node.label) + 4 + node.pref_height = 1 + node.min_height = 1 + + else: + # Generic: just measure children + total_h = 0 + for child in node.children: + measure(child, available_width) + total_h += child.pref_height + node.pref_width = available_width + node.min_width = 1 + node.pref_height = max(total_h, 1) + node.min_height = 1 diff --git a/backend/uframe/paint.py b/backend/uframe/paint.py new file mode 100644 index 0000000..0a35f78 --- /dev/null +++ b/backend/uframe/paint.py @@ -0,0 +1,216 @@ +"""Paint pass — write IR nodes into the CharGrid as characters. + +Depth-first traversal: each node writes its content at its assigned +rect position. Containers recurse into children after drawing their +own structure (borders, etc.). +""" + +from __future__ import annotations + +import textwrap + +from uframe.chars import ( + DIVIDER_CHARS, GAUGE_FILLED, GAUGE_EMPTY, + STATUS_CHARS, STATUS_COLORS, sparkline_chars, +) +from uframe.grid import CharGrid, CellStyle +from uframe.ir import ( + IRNode, Page, Box, Row, Col, Spacer, Pad, + Heading, Text, Label, Divider, Link, ListNode, ListItem, + Gauge, Sparkline, Status, + HeadingLevel, DividerStyle, ListStyle, Align, +) + + +def _align_text(text: str, width: int, align: Align) -> str: + """Align text within a field of the given width.""" + if len(text) >= width: + return text[:width] + if align == Align.CENTER: + return text.center(width) + elif align == Align.RIGHT: + return text.rjust(width) + return text.ljust(width) + + +def _style_from_node(node: IRNode) -> CellStyle: + """Create a CellStyle from a node's style attributes.""" + return CellStyle( + fg=node.style.fg, + bg=node.style.bg, + bold=node.style.bold, + italic=node.style.italic, + underline=node.style.underline, + ) + + +def paint(node: IRNode, grid: CharGrid) -> None: + """Recursively paint an IR node and its children into the grid.""" + x, y, w = node.rect.x, node.rect.y, node.rect.w + + # Ensure grid is tall enough + grid.grow_height(y + node.rect.h) + + if isinstance(node, Page): + for child in node.children: + paint(child, grid) + + elif isinstance(node, Box): + # Draw the border + title_style = CellStyle(bold=True, fg=node.style.fg) + grid.draw_border(x, y, w, node.rect.h, + weight=node.weight, + title=node.title, + title_style=title_style) + # Paint children inside the border + for child in node.children: + paint(child, grid) + + elif isinstance(node, Row): + for child in node.children: + paint(child, grid) + + elif isinstance(node, Col): + for child in node.children: + paint(child, grid) + + elif isinstance(node, Spacer): + pass # Just empty space + + elif isinstance(node, Pad): + for child in node.children: + paint(child, grid) + + elif isinstance(node, Heading): + style = CellStyle(bold=True) + if node.level == HeadingLevel.H1: + style.fg = "0f0" # green + elif node.level == HeadingLevel.H2: + style.fg = "0cf" # cyan + elif node.level == HeadingLevel.H3: + style.fg = "88f" # light blue + + # Underline-style heading + grid.put_text(x, y, node.text[:w], style=style) + + elif isinstance(node, Text): + style = _style_from_node(node) + + if node.spans and any( + s.bold or s.italic or s.underline or s.fg or s.bg + for s in node.spans + ): + # Render with inline spans + col = x + row = y + for span in node.spans: + span_style = CellStyle( + fg=span.fg or style.fg, + bg=span.bg or style.bg, + bold=span.bold or style.bold, + italic=span.italic or style.italic, + underline=span.underline or style.underline, + ) + for ch in span.text: + if col >= x + w: + col = x + row += 1 + if grid.in_bounds(col, row): + grid.put(col, row, ch, style=span_style) + col += 1 + else: + # Simple text with word wrapping + wrapped = textwrap.wrap(node.content, width=w) if node.content else [""] + for i, line in enumerate(wrapped): + if y + i < grid.height: + text = _align_text(line, w, node.style.align) + grid.put_text(x, y + i, text, style=style) + + elif isinstance(node, Label): + style = _style_from_node(node) + key_style = CellStyle(bold=True, fg=style.fg, bg=style.bg) + # Key: value layout with padding + key_text = f"{node.key}:" + pad = max(1, 16 - len(key_text)) + grid.put_text(x, y, key_text, style=key_style) + grid.put_text(x + len(key_text) + pad, y, node.value, style=style) + + elif isinstance(node, Divider): + ds = node.divider_style + char = DIVIDER_CHARS.get(ds.name.lower(), "─") + style = CellStyle(fg="555") + for col in range(x, x + w): + grid.put(col, y, char, style=style) + + elif isinstance(node, Link): + style = CellStyle(fg="0cf", underline=True) + # In ASCII mode, display as [text]. In Micron, the emitter wraps with link syntax. + # Write just the display text — the link metadata goes on cells for Micron emission. + grid.put_text(x, y, node.display[:w], style=style, link=node.dest) + + elif isinstance(node, ListNode): + for child in node.children: + paint(child, grid) + + elif isinstance(node, ListItem): + style = _style_from_node(node) + # Parent determines bullet style — use a simple bullet for now + bullet = "• " + grid.put_text(x - 2, y, bullet, style=CellStyle(fg="888")) + # Wrap content + wrapped = textwrap.wrap(node.content, width=w) if node.content else [""] + for i, line in enumerate(wrapped): + if y + i < grid.height: + grid.put_text(x, y + i, line, style=style) + + elif isinstance(node, Gauge): + style = _style_from_node(node) + # Label + label_text = f"{node.label} " + grid.put_text(x, y, label_text, style=CellStyle(bold=True)) + + bar_x = x + len(label_text) + bar_w = min(node.bar_width, w - len(label_text) - 6) + + if bar_w > 0: + pct = min(node.value / node.max_val, 1.0) if node.max_val > 0 else 0 + filled = int(bar_w * pct) + + # Determine color based on thresholds + fg = "0f0" # green + if node.crit is not None and node.value >= node.crit: + fg = "f00" # red + elif node.warn is not None and node.value >= node.warn: + fg = "ff0" # yellow + + for i in range(bar_w): + if i < filled: + grid.put(bar_x + i, y, GAUGE_FILLED, style=CellStyle(fg=fg)) + else: + grid.put(bar_x + i, y, GAUGE_EMPTY, style=CellStyle(fg="555")) + + # Percentage + pct_text = f" {int(pct * 100)}%" + grid.put_text(bar_x + bar_w, y, pct_text, style=CellStyle(fg=fg)) + + elif isinstance(node, Sparkline): + style = _style_from_node(node) + label_text = f"{node.label} " + grid.put_text(x, y, label_text, style=CellStyle(bold=True)) + + spark_x = x + len(label_text) + chars = sparkline_chars(node.values, node.spark_width) + spark_style = CellStyle(fg="0cf") + for i, ch in enumerate(chars): + grid.put(spark_x + i, y, ch, style=spark_style) + + elif isinstance(node, Status): + char = STATUS_CHARS.get(node.state, "◌") + color = STATUS_COLORS.get(node.state, "888") + grid.put(x, y, char, style=CellStyle(fg=color)) + grid.put_text(x + 2, y, node.label) + + else: + # Generic: paint children + for child in node.children: + paint(child, grid) diff --git a/backend/uframe/parser.py b/backend/uframe/parser.py new file mode 100644 index 0000000..7d841b1 --- /dev/null +++ b/backend/uframe/parser.py @@ -0,0 +1,350 @@ +"""µFrame parser — .uf source text → IR tree. + +Line-oriented, indentation-based (2-space). Each line is parsed as: + (indent_level, keyword, arguments) + +Nesting is determined by indentation: children are indented deeper +than their parent. +""" + +from __future__ import annotations + +import re +import shlex +from typing import Sequence + +from uframe.errors import ParseError +from uframe.ir import ( + IRNode, Page, Box, Row, Col, Spacer, Pad, + Heading, Text, Label, Divider, Link, ListNode, ListItem, + Gauge, Sparkline, Status, Table, TextSpan, + BorderWeight, HeadingLevel, DividerStyle, ListStyle, Align, Style, +) + +# --------------------------------------------------------------------------- +# Tokenisation helpers +# --------------------------------------------------------------------------- + +_INDENT_RE = re.compile(r"^( *)") +_MODIFIER_RE = re.compile(r"@(\w+)\{([^}]*)\}") + + +def _indent_level(line: str) -> int: + """Count leading spaces and return indent level (2 spaces = 1 level).""" + spaces = len(_INDENT_RE.match(line).group(1)) # type: ignore[union-attr] + return spaces // 2 + + +def _split_args(text: str) -> list[str]: + """Split argument string respecting quoted tokens.""" + try: + return shlex.split(text) + except ValueError: + return text.split() + + +def parse_inline(content: str) -> list[TextSpan]: + """Parse @modifier{text} syntax into a list of TextSpan objects. + + Supported modifiers: @bold{}, @italic{}, @under{}, @color{hex}{}, + @bg{hex}{}. + """ + spans: list[TextSpan] = [] + pos = 0 + + # Match @modifier{content} — including nested @color{hex}{text} + pattern = re.compile( + r"@(bold|italic|under|color|bg)" + r"(?:\{([0-9a-fA-F]{3})\})?" # optional hex arg for color/bg + r"\{([^}]*)\}" + ) + + for m in pattern.finditer(content): + # Add plain text before this modifier + if m.start() > pos: + spans.append(TextSpan(text=content[pos:m.start()])) + + mod = m.group(1) + hex_arg = m.group(2) + inner = m.group(3) + + span = TextSpan(text=inner) + if mod == "bold": + span.bold = True + elif mod == "italic": + span.italic = True + elif mod == "under": + span.underline = True + elif mod == "color" and hex_arg: + span.fg = hex_arg + elif mod == "bg" and hex_arg: + span.bg = hex_arg + + spans.append(span) + pos = m.end() + + # Trailing plain text + if pos < len(content): + spans.append(TextSpan(text=content[pos:])) + + # If no modifiers found, return single plain span + if not spans: + spans.append(TextSpan(text=content)) + + return spans + + +# --------------------------------------------------------------------------- +# Line-level parsing — keyword dispatch +# --------------------------------------------------------------------------- + +def _parse_border_weight(s: str) -> BorderWeight: + return { + "light": BorderWeight.LIGHT, + "heavy": BorderWeight.HEAVY, + "double": BorderWeight.DOUBLE, + "rounded": BorderWeight.ROUNDED, + }.get(s.lower(), BorderWeight.LIGHT) + + +def _parse_divider_style(s: str) -> DividerStyle: + return { + "light": DividerStyle.LIGHT, + "heavy": DividerStyle.HEAVY, + "double": DividerStyle.DOUBLE, + "dash": DividerStyle.DASH, + "dot": DividerStyle.DOT, + }.get(s.lower(), DividerStyle.LIGHT) + + +def _parse_heading_level(s: str) -> HeadingLevel: + return { + "1": HeadingLevel.H1, + "2": HeadingLevel.H2, + "3": HeadingLevel.H3, + }.get(s, HeadingLevel.H1) + + +def _parse_list_style(s: str) -> ListStyle: + return { + "bullet": ListStyle.BULLET, + "dash": ListStyle.DASH, + "number": ListStyle.NUMBER, + "arrow": ListStyle.ARROW, + }.get(s.lower(), ListStyle.BULLET) + + +def _parse_line(keyword: str, args: list[str], line_num: int) -> IRNode: + """Parse a single line into an IR node based on the keyword.""" + + if keyword == "page": + title = args[0] if args else "Untitled" + width = int(args[1]) if len(args) > 1 else 64 + return Page(title=title, width=width, source_line=line_num) + + elif keyword == "box": + # box [weight] "title" + if len(args) >= 2: + weight = _parse_border_weight(args[0]) + title = args[1] + elif len(args) == 1: + # Could be weight or title + if args[0].lower() in ("light", "heavy", "double", "rounded"): + weight = _parse_border_weight(args[0]) + title = "" + else: + weight = BorderWeight.LIGHT + title = args[0] + else: + weight = BorderWeight.LIGHT + title = "" + return Box(title=title, weight=weight, source_line=line_num) + + elif keyword == "row": + gap = int(args[0]) if args else 1 + return Row(gap=gap, source_line=line_num) + + elif keyword == "col": + w = int(args[0]) if args else None + return Col(col_width=w, source_line=line_num) + + elif keyword == "spacer": + lines = int(args[0]) if args else 1 + return Spacer(lines=lines, source_line=line_num) + + elif keyword == "pad": + vals = [int(a) for a in args[:4]] + while len(vals) < 4: + vals.append(0) + return Pad(top=vals[0], right=vals[1], bottom=vals[2], left=vals[3], + source_line=line_num) + + elif keyword == "heading": + level_str = args[0] if args else "1" + text = args[1] if len(args) > 1 else "" + return Heading(level=_parse_heading_level(level_str), text=text, + source_line=line_num) + + elif keyword == "text": + content = args[0] if args else "" + spans = parse_inline(content) + return Text(content=content, spans=spans, source_line=line_num) + + elif keyword == "label": + key = args[0] if args else "" + value = args[1] if len(args) > 1 else "" + return Label(key=key, value=value, source_line=line_num) + + elif keyword == "divider": + style = _parse_divider_style(args[0]) if args else DividerStyle.LIGHT + return Divider(divider_style=style, source_line=line_num) + + elif keyword == "link": + display = args[0] if args else "" + dest = args[1] if len(args) > 1 else "" + return Link(display=display, dest=dest, source_line=line_num) + + elif keyword == "list": + style = _parse_list_style(args[0]) if args else ListStyle.BULLET + return ListNode(list_style=style, source_line=line_num) + + elif keyword == "item": + content = args[0] if args else "" + return ListItem(content=content, source_line=line_num) + + # Style modifiers (applied to parent) + elif keyword == "align": + val = args[0].lower() if args else "left" + align = {"left": Align.LEFT, "center": Align.CENTER, "right": Align.RIGHT}.get(val, Align.LEFT) + return _StyleDirective("align", align, line_num) + + elif keyword == "color": + return _StyleDirective("fg", args[0] if args else None, line_num) + + elif keyword == "bg": + return _StyleDirective("bg", args[0] if args else None, line_num) + + elif keyword == "bold": + return _StyleDirective("bold", True, line_num) + + elif keyword == "italic": + return _StyleDirective("italic", True, line_num) + + elif keyword == "underline": + return _StyleDirective("underline", True, line_num) + + # Phase 4 placeholders + elif keyword == "gauge": + label = args[0] if args else "" + value = float(args[1]) if len(args) > 1 else 0 + max_val = float(args[2]) if len(args) > 2 else 100 + bar_width = int(args[3]) if len(args) > 3 else 28 + # Parse warn=N crit=N from remaining args + warn = crit = None + for a in args[4:]: + if a.startswith("warn="): + warn = float(a[5:]) + elif a.startswith("crit="): + crit = float(a[5:]) + return Gauge(label=label, value=value, max_val=max_val, + bar_width=bar_width, warn=warn, crit=crit, + source_line=line_num) + + elif keyword == "sparkline": + label = args[0] if args else "" + vals_str = args[1] if len(args) > 1 else "" + values = [float(v) for v in vals_str.split(",") if v.strip()] if vals_str else [] + width = int(args[2]) if len(args) > 2 else 20 + return Sparkline(label=label, values=values, spark_width=width, + source_line=line_num) + + elif keyword == "status": + label = args[0] if args else "" + state = args[1] if len(args) > 1 else "unknown" + return Status(label=label, state=state, source_line=line_num) + + else: + raise ParseError(f"Unknown keyword: {keyword!r}", line=line_num) + + +# --------------------------------------------------------------------------- +# Style directives — pseudo-nodes that modify their parent's style +# --------------------------------------------------------------------------- + +class _StyleDirective(IRNode): + """Temporary node representing a style modifier (align, color, bold, etc.). + + These are absorbed by the parent during tree building and never appear + in the final IR tree. + """ + def __init__(self, attr: str, value: object, line_num: int): + super().__init__(source_line=line_num) + self.attr = attr + self.value = value + + +# --------------------------------------------------------------------------- +# Tree builder +# --------------------------------------------------------------------------- + +def parse(source: str) -> Page: + """Parse a .uf source string into an IR tree rooted at a Page node. + + Returns the Page node with all children attached. + """ + lines = source.split("\n") + + # Stack: list of (indent_level, node) + stack: list[tuple[int, IRNode]] = [] + root: Page | None = None + + for line_num, raw_line in enumerate(lines, start=1): + # Skip blank lines and comments + stripped = raw_line.strip() + if not stripped or stripped.startswith("#"): + continue + + indent = _indent_level(raw_line) + + # Split into keyword + arguments + parts = stripped.split(None, 1) + keyword = parts[0].lower() + arg_str = parts[1] if len(parts) > 1 else "" + args = _split_args(arg_str) + + # Parse this line into a node + node = _parse_line(keyword, args, line_num) + + # Pop stack back to find the parent (parent indent < this indent) + while stack and stack[-1][0] >= indent: + stack.pop() + + if isinstance(node, _StyleDirective): + # Apply style directive to the current top of stack (parent) + if stack: + parent = stack[-1][1] + setattr(parent.style, node.attr, node.value) + continue + + # Attach to parent + if stack: + parent = stack[-1][1] + parent.children.append(node) + elif isinstance(node, Page): + root = node + else: + # Auto-wrap in a default Page if source doesn't start with `page` + root = Page(title="Untitled", width=64, source_line=0) + root.children.append(node) + stack.append((-1, root)) + + # Push onto stack + if isinstance(node, Page) and root is node: + stack.append((-1, node)) + else: + stack.append((indent, node)) + + if root is None: + root = Page(title="Untitled", width=64, source_line=0) + + return root diff --git a/backend/uframe/tests/__init__.py b/backend/uframe/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/uframe/tests/test_compile.py b/backend/uframe/tests/test_compile.py new file mode 100644 index 0000000..7875259 --- /dev/null +++ b/backend/uframe/tests/test_compile.py @@ -0,0 +1,212 @@ +"""End-to-end tests for the µFrame compile pipeline.""" + +import uframe + + +def test_empty_source(): + result = uframe.compile("") + assert result.ascii == "" + assert result.micron == "" + + +def test_simple_heading(): + result = uframe.compile('page "Test" 40\n heading 1 "Hello World"') + assert "Hello World" in result.ascii + assert "Hello World" in result.micron + + +def test_box_with_title(): + source = '''page "Demo" 40 + box light "Status" + text "All systems go"''' + result = uframe.compile(source) + # ASCII should have box-drawing characters + assert "┌" in result.ascii + assert "└" in result.ascii + assert "Status" in result.ascii + assert "All systems go" in result.ascii + + +def test_box_heavy(): + source = '''page "Demo" 40 + box heavy "Alert" + text "Warning"''' + result = uframe.compile(source) + assert "┏" in result.ascii + assert "Alert" in result.ascii + + +def test_box_double(): + source = '''page "Demo" 40 + box double "Title" + text "Content"''' + result = uframe.compile(source) + assert "╔" in result.ascii + assert "Title" in result.ascii + + +def test_box_rounded(): + source = '''page "Demo" 40 + box rounded "Panel" + text "Inside"''' + result = uframe.compile(source) + assert "╭" in result.ascii + assert "Panel" in result.ascii + + +def test_row_with_columns(): + source = '''page "Demo" 40 + row 2 + col 18 + text "Left" + col 18 + text "Right"''' + result = uframe.compile(source) + assert "Left" in result.ascii + assert "Right" in result.ascii + + +def test_label(): + source = '''page "Demo" 40 + label "Name" "Alice"''' + result = uframe.compile(source) + assert "Name:" in result.ascii + assert "Alice" in result.ascii + + +def test_divider(): + source = '''page "Demo" 40 + divider heavy''' + result = uframe.compile(source) + assert "━" in result.ascii + + +def test_link(): + source = '''page "Demo" 40 + link "Home" "/page/index.mu"''' + result = uframe.compile(source) + assert "Home" in result.ascii + # Micron should have link syntax + assert "index.mu" in result.micron + + +def test_list(): + source = '''page "Demo" 40 + list bullet + item "First" + item "Second"''' + result = uframe.compile(source) + assert "First" in result.ascii + assert "Second" in result.ascii + + +def test_spacer(): + source = '''page "Demo" 40 + text "Before" + spacer 2 + text "After"''' + result = uframe.compile(source) + lines = result.ascii.split("\n") + # Should have blank lines between Before and After + before_idx = next(i for i, l in enumerate(lines) if "Before" in l) + after_idx = next(i for i, l in enumerate(lines) if "After" in l) + assert after_idx - before_idx >= 3 # at least 2 blank lines between + + +def test_nested_boxes(): + source = '''page "Demo" 40 + box light "Outer" + box heavy "Inner" + text "Deep"''' + result = uframe.compile(source) + assert "Outer" in result.ascii + assert "Inner" in result.ascii + assert "Deep" in result.ascii + + +def test_micron_has_style_tags(): + source = '''page "Demo" 40 + heading 1 "Title"''' + result = uframe.compile(source) + # Micron should contain color tags for the heading + assert "`F" in result.micron or "Title" in result.micron + + +def test_gauge(): + source = '''page "Demo" 40 + gauge "CPU" 62 100 20 warn=75 crit=90''' + result = uframe.compile(source) + assert "CPU" in result.ascii + assert "█" in result.ascii + assert "62%" in result.ascii + + +def test_comment_ignored(): + source = '''page "Demo" 40 + # this is a comment + text "Visible"''' + result = uframe.compile(source) + assert "Visible" in result.ascii + assert "comment" not in result.ascii + + +def test_inline_modifiers(): + source = '''page "Demo" 40 + text "Hello @bold{world} today"''' + result = uframe.compile(source) + assert "Hello" in result.ascii + assert "world" in result.ascii + # Micron should have bold tags around "world" + assert "`!" in result.micron + + +def test_status(): + source = '''page "Demo" 40 + status "Server" online''' + result = uframe.compile(source) + assert "●" in result.ascii + assert "Server" in result.ascii + + +def test_full_dashboard(): + """Integration test: a realistic dashboard layout.""" + source = '''page "Node Status" 60 + box double "Relay Alpha-7" + align center + text "Reticulum Network Node" + + spacer + + heading 1 "Resources" + + gauge "CPU" 62 100 28 warn=75 crit=90 + gauge "MEM" 84 100 28 warn=80 crit=95 + + spacer + + heading 2 "Peers" + + label "Active" "7 / 12" + status "East Relay" online + status "South Bridge" online + status "Node Gamma" degraded + + divider heavy + + link "Home" "/page/index.mu"''' + + result = uframe.compile(source) + + # Verify key elements are present + assert "Relay Alpha-7" in result.ascii + assert "╔" in result.ascii # double box + assert "CPU" in result.ascii + assert "MEM" in result.ascii + assert "█" in result.ascii # gauge bars + assert "●" in result.ascii # status indicators + assert "━" in result.ascii # heavy divider + assert "Home" in result.ascii + + # Micron should have color tags + assert "`F" in result.micron + assert result.micron # non-empty diff --git a/docs/dynamic-templates.md b/docs/dynamic-templates.md new file mode 100644 index 0000000..3dd9d41 --- /dev/null +++ b/docs/dynamic-templates.md @@ -0,0 +1,883 @@ +# µFrame Dynamic Templates — Making Rich UIs Live on NomadNet + +## Addendum to the µFrame Design Document (v3) + +--- + +## 1. The NomadNet Dynamic Page Model + +Understanding how NomadNet serves dynamic pages is essential to +understanding how µFrame templates become live applications. + +### How it works + +NomadNet has a simple but powerful execution model, analogous to +CGI on the early web: + +``` + Client Node Server + ────── ─────────── + 1. Browse to /page/dashboard.mu + ───────────▶ + 2. Is dashboard.mu executable? + YES → run it + NO → send contents as-is + + 3. Execute: #!/usr/bin/env python3 + Script prints Micron to stdout + + 4. Capture stdout → send to client + ◀─────────── + 5. Render Micron in terminal +``` + +Key mechanics: + +- A `.mu` file without the execute bit is **static** — NomadNet + sends its contents directly to the browsing client +- A `.mu` file with the execute bit set is **dynamic** — NomadNet + runs it as a subprocess and serves whatever it prints to stdout +- The shebang line (`#!/usr/bin/env python3`) determines the + interpreter — Python, Bash, Lua, Rust, anything that runs +- The script must **terminate** — it cannot wait for input or + run indefinitely +- Cache behavior is controlled via a header line: `#!c=0` means + never cache (always re-execute), `#!c=300` means cache for 5 min + +### Form data flow + +Micron form fields (`\``, `\`<^|group|val\`label>`, +etc.) collect user input. When the user clicks a link on a page +that contains form fields, the field data is submitted along with +the link request: + +``` + Page A (has form fields + a submit link) + + ┌───────────────────────────────────────────────┐ + │ Name: `<24|name`Enter name...> │ + │ Role: `<^|role|admin`Admin> `<^|role|user`User> │ + │ │ + │ `[Submit`:/page/handle.mu] │ + └───────────────────────────────────────────────┘ + + User fills in "Alice", selects "Admin", clicks Submit + │ + ▼ + Page B (handle.mu) — receives field data via environment + variables, generates a response page with Micron output +``` + +The submitted field data is passed to the executable script +through environment variables in the format: + +``` + FIELD_name=Alice + FIELD_role=admin +``` + +Or via stdin as a URL-encoded or structured data payload +(implementation varies by NomadNet version). The executable +script reads these values and uses them to generate its output. + +--- + +## 2. µFrame's Dynamic Compilation Model + +Here is the key insight: **µFrame doesn't just emit static +Micron text — it can compile `.uf` templates into executable +Python scripts that generate Micron at request time.** + +This gives us three output modes: + +``` + .uf source ──▶ Parser ──▶ IR ──┬──▶ Plain ASCII (static preview) + ├──▶ Static .mu (static page) + └──▶ Dynamic .mu (executable script) + │ + ▼ + #!/usr/bin/env python3 + # Auto-generated by µFrame + # from: dashboard.uf + import os, sys, json, subprocess + ... + print(rendered_micron) +``` + +The dynamic output is a self-contained Python script with: +- The µFrame rendering engine embedded (or imported) +- Data-fetching hooks that run at request time +- Form field processing from environment variables +- Conditional rendering based on submitted data +- The full ASCII art + Micron generation pipeline + +### The three modes compared + +``` + ┌──────────────────────────────────────────────────────────────┐ + │ µFrame Output Modes │ + ├────────────────┬───────────────┬─────────────────────────────┤ + │ Plain ASCII │ Static .mu │ Dynamic .mu │ + ├────────────────┼───────────────┼─────────────────────────────┤ + │ Box-drawing │ Box-drawing │ Box-drawing │ + │ Block chars │ Block chars │ Block chars │ + │ Braille │ Braille │ Braille │ + │ │ + Color │ + Color │ + │ │ + Bold/italic │ + Bold/italic │ + │ │ + Links │ + Links │ + │ │ + Form fields │ + LIVE form fields │ + │ │ │ + Server-side data binding │ + │ │ │ + Conditional rendering │ + │ │ │ + Form submission handling │ + │ │ │ + System data at render time │ + │ │ │ + State persistence │ + ├────────────────┼───────────────┼─────────────────────────────┤ + │ Local terminal │ NomadNet page │ NomadNet live application │ + │ preview │ (cached) │ (re-executed per request) │ + └────────────────┴───────────────┴─────────────────────────────┘ +``` + +--- + +## 3. DSL Extensions for Dynamic Behavior + +### 3.1 Data Sources — `source` blocks + +A `source` block declares where live data comes from. At render +time, the generated script executes the source and binds the +result to a variable. + +``` +# Shell command — output captured as string +source cpu_pct : shell "grep 'cpu ' /proc/stat | awk '{print int(($2+$4)*100/($2+$4+$5))}'" +source mem_pct : shell "free | awk '/Mem/{print int($3/$2*100)}'" +source uptime : shell "uptime -p" +source peers : shell "rnstatus -j | python3 -c 'import sys,json; d=json.load(sys.stdin); print(len(d.get(\"peers\",[]))); '" + +# File read — contents loaded as string or parsed as JSON +source motd : file "/etc/motd" +source config : json "/home/node/.nomadnetwork/config.json" + +# Python expression — evaluated inline +source timestamp : python "datetime.now().strftime('%Y-%m-%d %H:%M')" +source rand_hex : python "secrets.token_hex(4)" + +# RNS/Reticulum API — direct integration +source peer_list : rns "peers" +source node_info : rns "identity" +``` + +Sources are resolved **at page render time** — every time a +client requests the page, the commands run fresh. + +Usage in templates: + +``` +gauge "CPU" $cpu_pct 100 28 warn=75 crit=90 +gauge "MEM" $mem_pct 100 28 warn=80 crit=95 +label "Uptime" "$uptime" +label "Peers" "$peers active" +text "Last updated: $timestamp" +``` + +### 3.2 Form Handling — `on_submit` blocks + +An `on_submit` block defines what happens when a form is +submitted. It receives field values and controls what the +page renders in response. + +``` +page "Search" 64 + + form "search" + field "query" 30 "Enter search term..." + radio "scope" "Local" | "Network" | "All" + button "Search" "/page/search.mu" + + on_submit "search" + # $query and $scope are now populated from submitted form data + source results : shell "search_index.py '$query' --scope '$scope'" + + heading 2 "Results for: $query" + + if $results + text "$results" + else + text "No results found." +``` + +The generated Python script handles this as: + +```python +#!/usr/bin/env python3 +#!c=0 +import os, subprocess + +# Read submitted form data +query = os.environ.get("FIELD_query", "") +scope = os.environ.get("FIELD_scope", "Local") + +if query: + # Form was submitted — render results + results = subprocess.check_output( + ["search_index.py", query, "--scope", scope] + ).decode().strip() + # ... render results template with Micron ... +else: + # No submission — render the form + # ... render form template with Micron ... +``` + +### 3.3 Conditional Rendering — `if` / `else` / `elif` + +``` +source disk_pct : shell "df / | awk 'NR==2{print int($5)}'" + +if $disk_pct > 90 + box heavy "DISK CRITICAL" + color f00 + gauge "Disk" $disk_pct 100 40 crit=90 + text "@bold{@color{f00}{Immediate action required!}}" +elif $disk_pct > 75 + box light "Disk Warning" + color ff0 + gauge "Disk" $disk_pct 100 40 warn=75 +else + gauge "Disk" $disk_pct 100 40 +``` + +### 3.4 Iteration — `for` loops + +``` +source peer_json : shell "rnstatus --json-peers" + +heading 2 "Active Peers" + +for peer in $peer_json + row 2 + col 30 + text "$peer.name" + col 10 + status "$peer.name" $peer.state + col 10 + text "$peer.latency" +``` + +The `for` construct works with JSON arrays or newline-delimited +text from shell commands. + +### 3.5 State Persistence — `state` blocks + +Since each page request is a fresh script execution, state must +be stored externally. µFrame provides a simple key-value store +backed by a JSON file on the node: + +``` +state "counter" "/tmp/uframe_counter.json" + +# Read +let visits = $counter.visits || 0 + +# Write (increments on each page load) +set counter.visits = $visits + 1 + +text "This page has been viewed $counter.visits times." +``` + +For form-driven state (e.g., a guestbook): + +``` +state "guestbook" "/var/nomadnet/guestbook.json" + +form "sign" + field "name" 20 "Your name..." + field "message" 40 "Your message..." + button "Sign" "/page/guestbook.mu" + +on_submit "sign" + append guestbook.entries { name: $name, message: $message, time: $timestamp } + text "@color{0f0}{Thanks, $name! Your message has been saved.}" + +heading 2 "Guestbook ($guestbook.entries.length entries)" + +for entry in $guestbook.entries + box light + text "@bold{$entry.name} — @italic{$entry.time}" + text "$entry.message" + spacer +``` + +### 3.6 Page Navigation with Data — `link` with parameters + +Links can pass data to the target page via query-style encoding: + +``` +# Simple navigation +link "Home" "/page/index.mu" + +# Navigation with parameters +link "View Peer $peer.name" "/page/peer_detail.mu?hash=$peer.hash" + +# In the target page, access with: +source peer_hash : param "hash" +``` + +### 3.7 Cache Control + +``` +page "Dashboard" 64 + cache 0 # never cache — always re-execute + # cache 60 # cache for 60 seconds + # cache none # alias for 0 + + source cpu : shell "..." + ... +``` + +Translates to the Micron header `#!c=0` in the first line of output. + +--- + +## 4. Compilation Pipeline — .uf to Executable .mu + +### 4.1 What the compiler generates + +A `.uf` file with dynamic features compiles into a Python script +that: + +1. Sets the shebang and cache header +2. Imports required modules +3. Reads environment variables (form data) +4. Executes source commands (shell, file, python, rns) +5. Evaluates conditionals and loops +6. Renders the IR tree into a CharGrid +7. Emits the CharGrid as Micron with style tags +8. Prints to stdout + +```python +#!/usr/bin/env python3 +#!c=0 +# Auto-generated by µFrame from dashboard.uf +# Do not edit — regenerate with: uframe compile dashboard.uf + +import os, sys, json, subprocess, datetime, secrets + +# ─── µFrame Runtime (embedded) ─────────────────────────────── + +class CharGrid: + """2D character grid with style annotations.""" + def __init__(self, width, height): + self.w = width + self.h = height + self.chars = [[' ']*width for _ in range(height)] + self.styles = [[None]*width for _ in range(height)] + + def put(self, x, y, ch, style=None): + if 0 <= x < self.w and 0 <= y < self.h: + self.chars[y][x] = ch + self.styles[y][x] = style + + def box(self, x, y, w, h, weight='light', title=None, title_style=None): + """Draw a box with automatic border characters.""" + # ... border drawing logic ... + + def gauge(self, x, y, w, value, max_val, label=None, + warn=None, crit=None): + """Render a horizontal gauge bar with threshold colors.""" + pct = min(value / max_val, 1.0) + filled = int(w * pct) + for i in range(w): + ch = '█' if i < filled else '░' + fg = None + if crit and value >= crit: fg = 'f00' + elif warn and value >= warn: fg = 'ff0' + elif i < filled: fg = '0f0' + else: fg = '555' + self.put(x + i, y, ch, {'fg': fg}) + # ... label and percentage ... + + def sparkline(self, x, y, w, values): + """Render braille sparkline from value array.""" + # ... braille pattern generation ... + + def emit_micron(self): + """Scan grid and emit Micron with style tags.""" + lines = [] + for row_idx in range(self.h): + line = [] + cur_style = None + for col_idx in range(self.w): + ch = self.chars[row_idx][col_idx] + st = self.styles[row_idx][col_idx] + if st != cur_style: + # Close previous style tags + if cur_style: + if cur_style.get('fg'): line.append('`f') + if cur_style.get('bold'): line.append('`!') + # Open new style tags + if st: + if st.get('bold'): line.append('`!') + if st.get('fg'): line.append(f'`F{st["fg"]}') + cur_style = st + line.append(ch) + # Close final style + if cur_style: + if cur_style.get('fg'): line.append('`f') + if cur_style.get('bold'): line.append('`!') + lines.append(''.join(line).rstrip()) + return '\n'.join(lines) + +# ─── Form Data ─────────────────────────────────────────────── + +def get_field(name, default=''): + """Read submitted form field from environment.""" + return os.environ.get(f'FIELD_{name}', default) + +def get_param(name, default=''): + """Read URL parameter.""" + return os.environ.get(f'PARAM_{name}', + os.environ.get(f'var_{name}', default)) + +# ─── Data Sources ──────────────────────────────────────────── + +def shell(cmd): + """Execute shell command, return stdout.""" + try: + return subprocess.check_output( + cmd, shell=True, timeout=5 + ).decode().strip() + except Exception: + return '' + +# ─── Resolve Sources ───────────────────────────────────────── + +cpu_pct = int(shell( + "grep 'cpu ' /proc/stat | awk '{print int(($2+$4)*100/($2+$4+$5))}'" +) or 0) +mem_pct = int(shell( + "free | awk '/Mem/{print int($3/$2*100)}'" +) or 0) +uptime_str = shell("uptime -p") +peer_count = shell("rnstatus -j 2>/dev/null | python3 -c " + "'import sys,json; print(len(json.load(sys.stdin).get(\"peers\",[])))'") +timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M') + +# ─── Build Grid & Render ──────────────────────────────────── + +grid = CharGrid(66, 40) + +# ... all the box(), gauge(), sparkline(), text() calls +# ... exactly as the layout engine would produce them ... + +# ─── Output ────────────────────────────────────────────────── + +print('#!c=0') # cache header: never cache +print(grid.emit_micron()) +``` + +### 4.2 CLI usage + +```bash +# Compile to dynamic executable .mu +uframe compile dashboard.uf --out dashboard.mu +chmod +x dashboard.mu + +# Compile and deploy in one step +uframe deploy dashboard.uf +# → renders, sets +x, copies to ~/.nomadnetwork/storage/pages/ + +# Compile with embedded vs. imported runtime +uframe compile dashboard.uf --embed # single self-contained file +uframe compile dashboard.uf --import # requires uframe_runtime.py on node +``` + +### 4.3 Compilation modes + +``` +┌────────────────────────────────────────────────────────────────┐ +│ µFrame Compilation Modes │ +├────────────────┬───────────────┬───────────────────────────────┤ +│ uframe render │ uframe render │ uframe compile │ +│ --ascii │ --micron │ │ +├────────────────┼───────────────┼───────────────────────────────┤ +│ Static ASCII │ Static .mu │ Executable .mu (Python) │ +│ to stdout │ file │ file with +x │ +├────────────────┼───────────────┼───────────────────────────────┤ +│ All values │ All values │ source{} values fetched │ +│ resolved at │ resolved at │ at request time │ +│ render time │ render time │ │ +│ │ │ Form fields become live │ +│ │ │ on_submit{} blocks active │ +│ │ │ if/for evaluated per request │ +│ │ │ State persists across visits │ +└────────────────┴───────────────┴───────────────────────────────┘ +``` + +--- + +## 5. Complete Dynamic Example + +### 5.1 Source — `search_node.uf` + +``` +page "Node Search" 64 + cache 0 + + source timestamp : python "datetime.now().strftime('%H:%M:%S')" + source peer_count : shell "rnstatus 2>/dev/null | grep -c 'Peer'" + state "history" "/var/nomadnet/search_history.json" + + box double "Node Search" + align center + text "Find peers and pages on the Reticulum mesh" + text "@italic{$peer_count peers reachable · updated $timestamp}" + + spacer + + form "search" + field "query" 30 "Search term..." + radio "type" "Nodes" | "Pages" | "Files" + checkbox "cache" "Include cached results" + button "Search" "/page/search_node.mu" + + on_submit "search" + # Log the search + append history.queries { q: $query, type: $type, time: $timestamp } + + source results : shell "mesh_search.py '$query' --type '$type'" + source result_count : python "len('''$results'''.strip().splitlines())" + + divider light + + heading 2 "Results for \"$query\" ($result_count found)" + + if $result_count > 0 + for line in $results + source parts : python "'''$line'''.split('|')" + row 1 + col 28 + link "$parts.0" "/page/detail.mu?hash=$parts.1" + col 8 + text "$parts.2" + col 10 + status "$parts.0" $parts.3 + else + spacer + text "@center{@color{ff0}{No results found for \"$query\"}}" + spacer + + divider light + heading 3 "Recent Searches" + + for entry in $history.queries[-5:] + text " $entry.time $entry.q ($entry.type)" + + divider heavy + text "@center{@italic{Relay Alpha-7 · $timestamp}}" +``` + +### 5.2 What the client sees + +**Before submission** (form is empty): + +``` +╔══ Node Search ═══════════════════════════════════════════════╗ +║ Find peers and pages on the Reticulum mesh ║ +║ 7 peers reachable · updated 14:32:07 ║ +╚══════════════════════════════════════════════════════════════╝ + + Search: [ Search term...___________________ ] + Type: (•) Nodes ( ) Pages ( ) Files + [ ] Include cached results + + `[`!Search`!`:/page/search_node.mu] + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Relay Alpha-7 · 14:32:07 +``` + +**After submitting "relay"** (script re-executes with FIELD_query=relay): + +``` +╔══ Node Search ═══════════════════════════════════════════════╗ +║ Find peers and pages on the Reticulum mesh ║ +║ 7 peers reachable · updated 14:32:15 ║ +╚══════════════════════════════════════════════════════════════╝ + + Search: [ relay__________________________ ] + Type: (•) Nodes ( ) Pages ( ) Files + [ ] Include cached results + + `[`!Search`!`:/page/search_node.mu] + + ────────────────────────────────────────────────────────────── + + >> Results for "relay" (3 found) + + `F0cfRelay-East`f 2 hops `F0f0●`f online + `F0cfRelay-South`f 4 hops `F0f0●`f online + `F0cfRelay-Backup`f 6 hops `Fff0◐`f degraded + + ────────────────────────────────────────────────────────────── + + >>> Recent Searches + 14:32:15 relay (Nodes) + 14:28:44 bridge (Pages) + 14:25:01 firmware (Files) + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + Relay Alpha-7 · 14:32:15 +``` + +The form fields retain submitted values, results appear below, +and the search history persists across visits via the state file. +Every piece of box-drawing, every colored indicator, every braille +sparkline renders identically in both ASCII preview and live Micron. + +--- + +## 6. Dynamic Patterns — A Cookbook + +### 6.1 Live Dashboard (auto-refresh via cache=0) + +``` +page "Status" 64 + cache 0 + + source cpu : shell "cat /proc/loadavg | awk '{print int($1*100/$(nproc))}'" + source mem : shell "free | awk '/Mem/{print int($3/$2*100)}'" + source net_in : shell "net_traffic.sh in" + source net_out : shell "net_traffic.sh out" + source net_history_in : shell "net_spark.sh in 20" + source net_history_out : shell "net_spark.sh out 20" + + box heavy "System Status" + row 2 + gauge "CPU" $cpu 100 28 warn=75 crit=90 + gauge "MEM" $mem 100 28 warn=80 crit=95 + spacer + label "IN" "$net_in KB/s" + sparkline "IN" $net_history_in 28 + label "OUT" "$net_out KB/s" + sparkline "OUT" $net_history_out 28 + + text "@center{@italic{Press Ctrl+R to refresh}}" +``` + +Client hits the page → script runs → reads `/proc` → renders +gauges and sparklines with real data → client sees it. +Ctrl+R re-requests → fresh execution → updated values. + +### 6.2 Guestbook with Persistent State + +``` +page "Guestbook" 64 + cache 0 + + state "gb" "/var/nomadnet/guestbook.json" + source timestamp : python "datetime.now().strftime('%Y-%m-%d %H:%M')" + + heading 1 "Guestbook" + + form "sign" + field "name" 20 "Your name" + field "msg" 40 "Leave a message..." + button "Sign" "/page/guestbook.mu" + + on_submit "sign" + if $name && $msg + prepend gb.entries { name: $name, msg: $msg, time: $timestamp } + text "@color{0f0}{✓ Thanks, $name!}" + + divider light + + for entry in $gb.entries[:20] + box rounded + text "@bold{$entry.name} @italic{@color{888}{$entry.time}}" + text "$entry.msg" + spacer +``` + +### 6.3 Multi-Page Wizard with Navigation + +``` +# Page 1: setup.mu +page "Setup Wizard — Step 1" 64 + cache 0 + + heading 1 "Network Configuration" + form "net" + field "interface" 20 "eth0" + radio "mode" "Auto" | "Manual" | "Mesh Only" + button "Next →" "/page/setup_2.mu" + +# Page 2: setup_2.mu +page "Setup Wizard — Step 2" 64 + cache 0 + + source iface : param "interface" # or field from previous page + source mode : param "mode" + + heading 1 "Confirm Settings" + label "Interface" "$iface" + label "Mode" "$mode" + + form "confirm" + checkbox "apply_now" "Apply immediately" + button "← Back" "/page/setup.mu" + button "Finish ✓" "/page/setup_done.mu?interface=$iface&mode=$mode" +``` + +### 6.4 Chat Room (community pattern) + +``` +page "Chat" 64 + cache 0 + + state "chat" "/var/nomadnet/chatlog.json" + source timestamp : python "datetime.now().strftime('%H:%M')" + + heading 1 "Node Chat" + + # Display last 15 messages + for msg in $chat.messages[-15:] + text "@bold{@color{$msg.color}{$msg.nick}} @color{888}{$msg.time}" + text " $msg.text" + + divider light + + form "send" + field "nick" 12 "Nickname" + field "text" 40 "Type message..." + button "Send" "/page/chat.mu" + + on_submit "send" + if $nick && $text + source color : python "format(hash('$nick')%4095,'03x')" + append chat.messages { nick: $nick, text: $text, time: $timestamp, color: $color } + + text "@center{@italic{@color{888}{Ctrl+R to refresh · $chat.messages.length messages}}}" +``` + +### 6.5 Interactive Data Explorer + +``` +page "Peer Explorer" 64 + cache 0 + + source peers_json : shell "rnstatus --json 2>/dev/null" + source selected : param "hash" + + heading 1 "Peer Explorer" + + table "Peers" + columns "Name" 24 | "Hops" 6 | "RTT" 8 | "Status" 10 + for peer in $peers_json.peers + row "$peer.name" | "$peer.hops" | "$peer.rtt" | "$peer.status" + + if $selected + divider heavy + source detail : shell "rnstatus --peer $selected --json 2>/dev/null" + + box double "Peer Detail: $detail.name" + label "Hash" "$detail.hash" + label "Address" "$detail.address" + label "Hops" "$detail.hops" + label "Latency" "$detail.rtt" + label "Last Seen" "$detail.last_seen" + label "Transport" "$detail.transport" + + sparkline "Latency (24h)" $detail.latency_history 40 + + row 2 + link "Ping" "/page/action.mu?cmd=ping&hash=$detail.hash" + link "Trace" "/page/action.mu?cmd=trace&hash=$detail.hash" + link "Browse" "$detail.hash:/page/index.mu" +``` + +--- + +## 7. Security Considerations + +Dynamic pages execute code on the node server. µFrame enforces: + +- **Shell command sanitization**: all `$variable` values interpolated + into shell commands are escaped with `shlex.quote()` to prevent + injection +- **State file isolation**: state files are restricted to a + configurable directory (default: `/var/nomadnet/uframe/`) +- **Execution timeout**: all shell sources have a default 5-second + timeout, configurable per source +- **No network egress by default**: source commands run in the + node's local context — they can read local system data but + µFrame does not add network capabilities beyond what the + scripts themselves invoke +- **Input validation**: field values are length-limited and + sanitized before use in sources or state operations + +``` +# In the DSL, explicit sanitization: +source result : shell "search.py" --arg $query --sanitize + timeout 10 + max_length 256 +``` + +--- + +## 8. Architecture Summary + +``` + ┌──────────────────────────────┐ + │ .uf Source │ + │ (layout + sources + forms │ + │ + conditionals + state) │ + └──────────────┬───────────────┘ + │ + ┌────▼────┐ + │ Parse │ + └────┬────┘ + │ + ┌────▼────┐ + │ IR │ + │ Tree │ + └──┬───┬──┘ + │ │ + ┌──────────────┘ └────────────────┐ + │ │ + ┌──────▼──────┐ ┌───────▼───────┐ + │ render mode │ │ compile mode │ + │ (immediate) │ │ (codegen) │ + └──┬───────┬──┘ └───────┬───────┘ + │ │ │ + ┌──────▼┐ ┌───▼─────┐ ┌───────▼────────┐ + │ ASCII │ │ Static │ │ Executable .mu │ + │ stdout│ │ .mu file│ │ Python script │ + └───────┘ └─────────┘ │ with embedded │ + │ runtime + │ + │ data sources + │ + │ form handling │ + └───────┬────────┘ + │ + chmod +x + deploy to + │ + ┌─────────▼─────────┐ + │ NomadNet Node │ + │ ~/.nomadnetwork/ │ + │ storage/pages/ │ + │ │ + │ Client request → │ + │ Execute script → │ + │ Stdout = Micron │ + │ with live data, │ + │ rich ASCII art, │ + │ color + forms │ + └───────────────────┘ +``` + +The dynamic model turns µFrame from a static template engine into a +**full application framework for NomadNet** — where the same DSL that +defines the visual layout also defines the data flow, user interaction, +and server-side logic. The ASCII art isn't decoration — it's the UI +of a live, interactive, decentralized application running over +encrypted mesh networks. diff --git a/docs/framework-design-v3.md b/docs/framework-design-v3.md new file mode 100644 index 0000000..291fc83 --- /dev/null +++ b/docs/framework-design-v3.md @@ -0,0 +1,889 @@ +# µFrame — A DSL for Rich Terminal UIs rendered as ASCII and Micron + +## Overview + +**µFrame** is a declarative DSL that compiles to rich terminal +interfaces built from Unicode box-drawing, block elements, braille +patterns, and careful spatial layout. It produces two outputs from +the same source: + +1. **Plain ASCII** — the raw visual layout, viewable in any terminal +2. **Micron `.mu`** — the same visual layout enhanced with Micron's + color, styling, links, and interactive form fields + +Both outputs share the same rich character art. Micron doesn't +degrade the visuals — it *elevates* them. The ASCII art passes +through verbatim into the `.mu` file, and Micron tags wrap it +with color, emphasis, alignment, and interactivity that plain +text cannot express. + +``` + ┌──────────────────────────────────┐ + ┌───▶│ Plain ASCII │ + │ │ Box drawing, braille, blocks │ + │ │ No color, no links, no forms │ + .uf ──▶ Parser ──▶ IR ─┤ └──────────────────────────────────┘ + │ ┌──────────────────────────────────┐ + └───▶│ Micron .mu │ + │ Same visual base │ + │ + `Fhex color`f │ + │ + `!bold`! `*italic`* │ + │ + `[links`/dest] │ + │ + `
│ + │ + `c alignment`a │ + └──────────────────────────────────┘ +``` + +The relationship is additive: + +``` + Plain ASCII = layout + structure + data viz + Micron = layout + structure + data viz + color + style + interaction +``` + +--- + +## 1. Design Philosophy + +### The terminal is the canvas — in both modes + +A NomadNet node browser is a terminal. A local shell is a terminal. +The character grid is the shared substrate. Unicode box-drawing, +block elements, and braille dots render identically in both +contexts. µFrame exploits this fully: + +- **Box drawing** (`┌─┐│└┘`) creates bordered panels, tables, + nested layouts — same characters in ASCII and Micron +- **Block elements** (`█▉▊▋▌▍▎▏░▒▓`) build bar charts, gauges, + heatmaps — passed through as literal text in Micron +- **Braille** (`⠀`–`⣿`, 256 patterns) gives 2×4 sub-cell resolution + for sparklines and dot plots — just text, works everywhere +- **Micron then paints on top**: colored bars, highlighted thresholds, + bold headers, clickable links, interactive form fields + +### What Micron adds beyond ASCII + +Micron's tag system maps perfectly onto the styling layer that +plain ASCII lacks: + +| Capability | Plain ASCII | Micron | +|--------------------|---------------------|---------------------------------------| +| Borders & boxes | ✓ box-drawing chars | ✓ same chars + colored with `Fhex` | +| Bar charts | ✓ block elements | ✓ same blocks + colored thresholds | +| Sparklines | ✓ braille dots | ✓ same braille + colored | +| Status indicators | ✓ ● ○ ◐ chars | ✓ same chars + `F0f0` green/red | +| Table data | ✓ monospace align | ✓ same alignment + bold headers | +| Emphasis | ✗ (no mechanism) | ✓ `!bold`! `*italic`* `_underline`_ | +| Color | ✗ (no mechanism)* | ✓ `Fhex text`f / `Bhex text`b | +| Alignment | manual spacing | ✓ `c center`a / `r right`a | +| Links | ✗ display only | ✓ `[click here`/page.mu] | +| Text input | ✗ display only | ✓ ` | +| Radio / checkbox | ✗ visual only | ✓ `<^|group|val`label> ` | +| Headings | manual styling | ✓ `>` / `>>` / `>>>` with styling | + +*ASCII mode can optionally emit ANSI escape codes with `--ansi`, +but the default is pure text. + +--- + +## 2. The Rendering Model + +Both renderers share a common **CharGrid** — a 2D matrix of +characters that represents the visual layout. They diverge only +in the final emission step. + +``` + ┌──────────┐ + .uf ──▶ Parse ──▶│ IR Tree │ + └────┬─────┘ + │ + ┌────▼─────┐ + │ Layout │ ◀── width resolution, row splits, + │ Engine │ border merging, chart rendering + └────┬─────┘ + │ + ┌────▼─────┐ + │ CharGrid │ ◀── 2D array of (char, style) pairs + │ + Styles │ style = {fg, bg, bold, italic, + └──┬────┬──┘ underline, link, field_meta} + │ │ + ┌───────▼┐ ┌▼─────────┐ + │ ASCII │ │ Micron │ + │ Emitter │ │ Emitter │ + └─────────┘ └──────────┘ + chars only chars + tags +``` + +### The CharGrid + +Every cell in the grid stores: + +``` +Cell: + char : string # the visible character (e.g. "█", "┌", "⣿") + fg : string|null # foreground color, 3-digit hex + bg : string|null # background color, 3-digit hex + bold : bool + italic : bool + underline : bool + link : string|null # destination path for clickable cells + field : FieldMeta|null # form field metadata for interactive cells +``` + +### ASCII Emitter + +Reads only `cell.char` from each cell. Produces a plain text file. +With `--ansi`, reads `fg`, `bg`, `bold`, `italic`, `underline` +and emits ANSI escape codes. + +### Micron Emitter + +Reads every cell property. Scans each line left-to-right, tracks +style state, and opens/closes Micron tags at style transitions: + +``` +Line scan: ┌── gauge "CPU" ──────────┐ +Chars: C P U █ █ █ ░ ░ 6 2 % +Styles: bold fg:0f0 fg:f00 + ↓ ↓ +Micron: `!CPU`! `F0f0███`f`Ff00░░`f `Ff0062%`f +``` + +This means the exact same box-drawing layout appears in both +outputs. The Micron version simply has color and emphasis tags +woven between the same characters. + +--- + +## 3. Visual Primitives — The Shared Toolkit + +Everything below renders identically in both ASCII and Micron. +The Micron output adds color/style annotations on top. + +### 3.1 Box Drawing + +Four border weights: + +``` +Light Heavy Double Rounded +┌──────┐ ┏━━━━━━┓ ╔══════╗ ╭──────╮ +│ │ ┃ ┃ ║ ║ │ │ +└──────┘ ┗━━━━━━┛ ╚══════╝ ╰──────╯ +``` + +Nested boxes with automatic junction merging: + +``` +┌─────────────────┬──────────┐ +│ Left Panel │ Right │ +│ │ │ +├─────────────────┴──────────┤ +│ Footer spans full width │ +└────────────────────────────┘ +``` + +Titled boxes (title inlined in top border): + +``` +┌─ System Health ─────────────────────────┐ +│ │ +└─────────────────────────────────────────┘ + +┏━ ALERT ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ ┃ +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +``` + +In Micron, the border characters are plain text. The title +can be wrapped in `!bold`! and `Fhex color`f tags. + +### 3.2 Data Visualization + +**Horizontal bars** — block elements with optional color thresholds: + +``` + ASCII: CPU ████████████████████░░░░░░░░░░ 62% + + Micron: CPU `F0f0████████████████████`f`F333░░░░░░░░░░`f 62% + ↑ green fill ↑ dim empty +``` + +**Gauges with thresholds** — color shifts at warn/crit boundaries: + +``` + ASCII: MEM █████████████████████████████░ 93% ⚠ + + Micron: MEM `Ff00█████████████████████████████`f░ `Ff00 93% ⚠`f + ↑ red because value > crit threshold +``` + +**Vertical bar charts**: + +``` + ASCII: Micron adds color per bar: + + █ `F08f█`f + █ █ `F08f█`f `F0f0█`f + █ █ █ █ `F08f█`f `Fff0█`f `F0f0█`f `Fff0█`f + █ █ █ █ █ █ `F08f█`f `Fff0█`f █ `F0f0█`f █ `Fff0█`f + █ █ █ █ █ █ █ █ ... + ───────────────── + 0 3 6 9 12 15 +``` + +**Sparklines** — braille characters for inline time series: + +``` + ASCII: NET ⣀⣤⣶⣿⣿⣷⣶⣤⣀⣀⣤⣶⣿⣷⣤⣀ avg 31% + + Micron: NET `F0ff⣀⣤⣶⣿⣿⣷⣶⣤⣀⣀⣤⣶⣿⣷⣤⣀`f avg 31% + ↑ cyan sparkline +``` + +**Heatmap** — using shade blocks with per-cell color: + +``` + Mon `F0f0░`f`F0f0░`f`F4f0▒`f`F8f0▓`f`Fff0█`f`F8f0▓`f`F4f0▒`f + Tue `F4f0▒`f`F8f0▓`f`Fff0█`f`Fff0█`f`Fff0█`f`F8f0▓`f`F4f0▒`f + Wed `F0f0░`f`F0f0░`f`F0f0░`f`F4f0▒`f`F4f0▒`f`F0f0░`f`F0f0░`f + 0 4 8 12 16 20 24 +``` + +In plain ASCII, same characters, no color — the shade density +still communicates intensity. + +**Status indicators** — colored in Micron, shape-coded in ASCII: + +``` + ASCII: ● Online ○ Offline ◐ Degraded ◌ Unknown + + Micron: `F0f0●`f Online `Ff00○`f Offline `Fff0◐`f Degraded +``` + +Both modes are readable — color adds clarity but shape carries +the information alone. + +### 3.3 Tables + +Tables use box-drawing for structure. Identical in both outputs. +Micron adds bold headers and colored status cells: + +``` + ASCII: + ┌──────────────────────┬──────┬─────────┬──────────┐ + │ Destination │ Hops │ Latency │ Status │ + ├──────────────────────┼──────┼─────────┼──────────┤ + │ a7f2::relay-east │ 2 │ 34ms │ ● alive │ + │ c4e1::bridge-south │ 4 │ 112ms │ ● alive │ + │ 01ab::node-gamma │ 7 │ 580ms │ ○ stale │ + └──────────────────────┴──────┴─────────┴──────────┘ + + Micron: + ┌──────────────────────┬──────┬─────────┬──────────┐ + │ `!Destination`! │`!Hops`!│`!Latency`!│`!Status`!│ + ├──────────────────────┼──────┼─────────┼──────────┤ + │ a7f2::relay-east │ 2 │ 34ms │ `F0f0●`f alive │ + │ c4e1::bridge-south │ 4 │ 112ms │ `F0f0●`f alive │ + │ 01ab::node-gamma │ 7 │ 580ms │ `Ff00○`f stale │ + └──────────────────────┴──────┴─────────┴──────────┘ +``` + +### 3.4 Form Elements + +In ASCII, forms are visual representations. In Micron, the same +characters appear but the input areas become live interactive +fields. + +``` + ASCII (visual only): + ┌─ Search ──────────────────────────────┐ + │ │ + │ Query: [ _________________________ ] │ + │ │ + │ Scope: (•) Local ( ) Network │ + │ [ ] Include offline nodes │ + │ │ + │ ┌──────────┐ │ + │ │ Search │ │ + │ └──────────┘ │ + └───────────────────────────────────────┘ + + Micron (interactive): + ┌─ `!Search`! ──────────────────────────┐ + │ │ + │ Query: `<32|query`Enter search...> │ + │ │ + │ Scope: `<^|scope|local|*`Local> `<^|scope|net`Network> + │ ` + │ │ + │ `[`!Search`!`:/action/search] + │ │ + └───────────────────────────────────────┘ +``` + +The border characters are identical. Micron replaces the +placeholder bracket notation with live form tags and turns +the button into a clickable link. + +--- + +## 4. The DSL — `.uf` Files + +### 4.1 Syntax + +- **Indentation** defines nesting (2-space) +- **Keywords** lead each line +- **Strings** in double quotes +- **Pipe `|`** separates inline list items +- **`$`** references variables +- **`#`** starts comments + +### 4.2 Layout Primitives + +``` +page "Title" [width] + # Root container. Default width: 64. + +box [weight] "Title" + # Bordered region. weight: light|heavy|double|rounded + # Title is inset in the top border. + # In Micron: title gets `!bold`!, border chars are literal. + +row [gap] + # Horizontal layout. Children split available width. + # gap: chars between children (default: 1) + +col [width] + # Explicit column in a row. Width in chars or percentage. + +spacer [lines] + # Vertical whitespace. Default: 1 + +pad [top] [right] [bottom] [left] + # Inner margin for a container. +``` + +### 4.3 Content Primitives + +``` +heading [1|2|3] "Text" + # Rendered with underline/box in ASCII. + # In Micron: > / >> / >>> plus `!bold`! + +text "Content with @bold{inline} @color{0f0}{modifiers}" + # @bold{...} → Micron `!...`! + # @italic{...} → Micron `*...*` + # @under{...} → Micron `_..._` + # @color{hex}{...} → Micron `Fhex...`f + # @bg{hex}{...} → Micron `Bhex...`b + # In ASCII: modifiers stripped (or ANSI with --ansi) + +label "Key" "Value" + # Aligned key-value pair. + +list [bullet|number|dash|arrow] + item "First" + item "Second" + +link "Display text" "/destination.mu" + # ASCII: [Display text] + # Micron: `[Display text`/destination.mu] + +divider [light|heavy|double|dash|dot] + # Full-width horizontal rule using appropriate chars. +``` + +### 4.4 Data Visualization Primitives + +``` +gauge "Label" [value] [max] [width] + # Horizontal progress bar. + # Thresholds: warn=[n] crit=[n] + # ASCII: Label ████████████░░░░ 62% + # Micron: same, with color shifts at thresholds + +meter "Label" [value] [max] + # Compact inline gauge (no border, just bar + %) + +bar_h "Label" [value] [max] [width] + # Single horizontal bar in a chart context. + +bar_v [height] + # Vertical bar chart container. + bar "Label" [value] + bar "Label" [value] + # Uses ▁▂▃▄▅▆▇█ stacked vertically. + +sparkline "Label" [values] [width] + # Braille-dot inline chart. + # values: comma-separated or $variable + +heatmap [rows] [cols] + # Grid of colored shade blocks. + # Uses ░▒▓█ for intensity. + # Micron adds per-cell `Fhex` color. + +status "Label" [online|offline|degraded|unknown|alert] + # Shape-coded indicator + label. + # Micron adds color to the indicator. + +table "Title" + columns "Name" [width] | "Name" [width] | ... + row "val" | "val" | ... + # Box-drawn table with header separator. + # Micron: bold headers, colored cells via @modifiers in values. +``` + +### 4.5 Form Primitives + +``` +form "name" + field "name" [width] "placeholder" + password "name" [width] "placeholder" + radio "group" "Opt A" | "Opt B" | "Opt C" + checkbox "name" "Label" + toggle "name" "Label" [on|off] + dropdown "name" "Opt A" | "Opt B" | "Opt C" + button "Label" ["/action/path"] + + # ASCII: visual placeholders (brackets, radio dots, etc.) + # Micron: live interactive fields using native form tags +``` + +### 4.6 Style Modifiers + +Applied as indented children of any node: + +``` +align [left|center|right] +color [3-digit hex] +bg [3-digit hex] +border [light|heavy|double|rounded|none] +bold +italic +underline +``` + +### 4.7 Variables & Components + +``` +# Variables +let name = "Relay Alpha-7" +let cpu_data = 42, 67, 55, 78, 91, 63, 48 + +# Component definition +component stat(label, value, max, trend) + box light "$label" + gauge "$label" $value $max 20 + text "@italic{$trend}" + +# Component usage +row 2 + stat "CPU" 62 100 "▲ +5%" + stat "MEM" 84 100 "▼ -2%" +``` + +--- + +## 5. Complete Example + +### 5.1 Source + +``` +let node = "Relay Alpha-7" +let uptime = "14d 3h 22m" + +page "$node" 66 + + box double "$node" + align center + text "Reticulum Network Node" + text "Online $uptime" + + spacer + + heading 1 "Resources" + + row 2 + col + gauge "CPU" 62 100 28 warn=75 crit=90 + gauge "GPU" 21 100 28 + col + gauge "MEM" 84 100 28 warn=80 crit=95 + gauge "SWP" 3 100 28 + + spacer + + heading 1 "Network" + + row 2 + col 40 + text "Traffic (60s)" + sparkline "IN" 1,3,5,8,7,5,3,2,1,3,6,8,7,4 20 + sparkline "OUT" 2,2,3,5,8,7,5,3,2,1,1,3,5,8 20 + col + label "Peers" "7 / 12" + status "East Relay" online + status "South Bridge" online + status "Node Gamma" degraded + + spacer + + heading 1 "Routing" + + table "Routes" + columns "Destination" 22 | "Hops" 6 | "RTT" 8 | "State" 10 + row "a7f2::relay-east" | "2" | "34ms" | "@color{0f0}{● alive}" + row "c4e1::bridge-south" | "4" | "112ms" | "@color{0f0}{● alive}" + row "01ab::node-gamma" | "7" | "580ms" | "@color{f00}{○ stale}" + row "f390::hub-north" | "1" | "8ms" | "@color{0f0}{● alive}" + + spacer + + heading 1 "Actions" + + box rounded "Quick Command" + form "cmd" + field "target" 30 "Destination hash..." + radio "mode" "Ping" | "Trace" | "Page" + checkbox "verbose" "Verbose output" + button "Execute" "/action/exec" + + divider heavy + + text "@center{© 2026 $node · Reticulum Network}" +``` + +### 5.2 ASCII Output + +``` +╔══ Relay Alpha-7 ═════════════════════════════════════════════════╗ +║ Reticulum Network Node ║ +║ Online 14d 3h 22m ║ +╚══════════════════════════════════════════════════════════════════╝ + + ── Resources ───────────────────────────────────────────────────── + + CPU ████████████████████░░░░░░░░ 62% MEM █████████████████████████░░ 84% ⚠ + GPU ██████░░░░░░░░░░░░░░░░░░░░░ 21% SWP █░░░░░░░░░░░░░░░░░░░░░░░░░ 3% + + ── Network ─────────────────────────────────────────────────────── + + Traffic (60s) Peers: 7 / 12 + IN ⣀⣤⣶⣿⣷⣶⣤⣀⣀⣤⣶⣿⣷⣤ 4.2 KB/s East Relay ● online + OUT ⣀⣀⣠⣤⣶⣿⣷⣶⣤⣀⣀⣠⣤⣶⣿ 2.1 KB/s South Bridge ● online + Node Gamma ◐ degraded + + ── Routing ─────────────────────────────────────────────────────── + + ┌────────────────────────┬────────┬──────────┬────────────┐ + │ Destination │ Hops │ RTT │ State │ + ├────────────────────────┼────────┼──────────┼────────────┤ + │ a7f2::relay-east │ 2 │ 34ms │ ● alive │ + │ c4e1::bridge-south │ 4 │ 112ms │ ● alive │ + │ 01ab::node-gamma │ 7 │ 580ms │ ○ stale │ + │ f390::hub-north │ 1 │ 8ms │ ● alive │ + └────────────────────────┴────────┴──────────┴────────────┘ + + ── Actions ─────────────────────────────────────────────────────── + + ╭─ Quick Command ──────────────────────────────────────────────╮ + │ │ + │ target: [ Destination hash...________________ ] │ + │ mode: (•) Ping ( ) Trace ( ) Page │ + │ [ ] Verbose output │ + │ ┌───────────┐ │ + │ │ Execute │ │ + │ └───────────┘ │ + ╰──────────────────────────────────────────────────────────────╯ + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + © 2026 Relay Alpha-7 · Reticulum Network +``` + +### 5.3 Micron Output + +The **same characters** — every box corner, every bar segment, every +braille dot — with Micron tags added for color and interactivity: + +``` +╔══ `!Relay Alpha-7`! ═════════════════════════════════════════════╗ +║`c Reticulum Network Node`a ║ +║`c Online 14d 3h 22m`a ║ +╚══════════════════════════════════════════════════════════════════╝ + +>Resources + + `!CPU`! `F0f0████████████████████`f`F555░░░░░░░░`f 62% `!MEM`! `Ff80█████████████████████████`f`F555░░`f `Ff8084%`f ⚠ + `!GPU`! `F0f0██████`f`F555░░░░░░░░░░░░░░░░░░░░░`f 21% `!SWP`! `F0f0█`f`F555░░░░░░░░░░░░░░░░░░░░░░░░░`f 3% + +>Network + + `!Traffic (60s)`! `!Peers:`! 7 / 12 + IN `F0cf⣀⣤⣶⣿⣷⣶⣤⣀⣀⣤⣶⣿⣷⣤`f 4.2 KB/s East Relay `F0f0●`f online + OUT `F0cf⣀⣀⣠⣤⣶⣿⣷⣶⣤⣀⣀⣠⣤⣶⣿`f 2.1 KB/s South Bridge `F0f0●`f online + Node Gamma `Fff0◐`f degraded + +>Routing + + ┌────────────────────────┬────────┬──────────┬────────────┐ + │ `!Destination`! │ `!Hops`! │ `!RTT`! │ `!State`! │ + ├────────────────────────┼────────┼──────────┼────────────┤ + │ a7f2::relay-east │ 2 │ 34ms │ `F0f0●`f alive │ + │ c4e1::bridge-south │ 4 │ 112ms │ `F0f0●`f alive │ + │ 01ab::node-gamma │ 7 │ 580ms │ `Ff00○`f stale │ + │ f390::hub-north │ 1 │ 8ms │ `F0f0●`f alive │ + └────────────────────────┴────────┴──────────┴────────────┘ + +>Actions + + ╭─ `!Quick Command`! ──────────────────────────────────────────╮ + │ │ + │ target: `<30|target`Destination hash...> │ + │ mode: `<^|mode|ping|*`Ping> `<^|mode|trace`Trace> `<^|mode|page`Page> + │ ` │ + │ `[`!Execute`!`:/action/exec] + │ │ + ╰──────────────────────────────────────────────────────────────╯ + +-━ + +`c© 2026 Relay Alpha-7 · Reticulum Network`a +``` + +Note how the table borders, box corners, and gauge characters +are **byte-for-byte identical** in both outputs. Micron simply +interleaves its backtick tags around the characters that need +color or emphasis. + +--- + +## 6. Intermediate Representation + +### IR Node + +``` +IRNode: + type : NodeType + label : string | null + children : IRNode[] + styles : { + fg : string | null # 3-digit hex + bg : string | null + bold : bool + italic : bool + underline: bool + align : left | center | right + border : light | heavy | double | rounded | none + } + layout : { + width : int | pct | null + height : int | null + gap : int + pad : [top, right, bottom, left] + } + data : { # type-specific + value : number | null + max : number | null + warn : number | null + crit : number | null + values : number[] | null # sparkline, bar_v + state : enum | null # status indicator + options : string[] | null # radio, dropdown + columns : Column[] | null # table + rows : Row[] | null # table + link : string | null # destination + field : FieldMeta | null # form metadata + } + inline : InlineSpan[] # parsed @modifiers +``` + +### The CharGrid + +``` +CharGrid: + width : int + height : int + cells : Cell[height][width] + +Cell: + char : char # visible character + style : CellStyle # for Micron emission + field : FieldMeta? # if this cell is part of a form field + link : string? # if this cell is clickable + +CellStyle: + fg : string? # 3-digit hex + bg : string? + bold : bool + italic : bool + underline : bool +``` + +The layout engine fills the CharGrid. Both emitters read it. +The ASCII emitter ignores the style layer. The Micron emitter +scans for style transitions and inserts tags. + +--- + +## 7. Rendering Pipeline + +``` +Phase 1: Parse + .uf source → token stream → IR tree + Variables resolved, components expanded. + +Phase 2: Measure + Bottom-up pass: compute min/preferred width and height + for each node. Leaf nodes (text, gauge, field) report + their intrinsic sizes. Containers aggregate children. + +Phase 3: Layout + Top-down pass: assign (x, y, w, h) to every node. + Row nodes divide width among columns. + Box nodes reserve border characters (1 char each side). + +Phase 4: Paint + Depth-first traversal. Each node writes characters into + the CharGrid at its assigned position: + - Box: draw border chars, set title style + - Gauge: compute bar length, write █ and ░, set fg color + based on thresholds + - Sparkline: convert values to braille patterns + - Table: draw grid, write cell content, set header bold + - Form: write visual placeholders, attach FieldMeta + - Status: write indicator char, set color by state + +Phase 5: Merge Borders + Post-pass: scan for adjacent border characters and replace + with correct junction characters (┬ ┴ ├ ┤ ┼ etc.). + Weight priority: double > heavy > light > rounded. + +Phase 6: Emit + ASCII: read cell.char for every cell, join into lines. + Micron: scan each line, diff style between adjacent cells, + open/close Micron tags at transitions. +``` + +### Border Merging Detail + +``` +Before: After: +┌────┐┌────┐ ┌────┬────┐ +│ ││ │ ──▶ │ │ │ +└────┘└────┘ └────┴────┘ + +┌────────┐ ┌────────┐ +│┌──────┐│ ├──────┐ │ (nested box shares +││ ││ ──▶ │ │ │ parent left edge) +│└──────┘│ ├──────┘ │ +└────────┘ └────────┘ +``` + +The merging pass checks each cell against its 4 neighbors +and selects from a lookup table of ~40 junction characters. + +--- + +## 8. Character Reference + +### Boxes + +``` +Light: ┌ ─ ┐ │ └ ┘ ├ ┤ ┬ ┴ ┼ +Heavy: ┏ ━ ┓ ┃ ┗ ┛ ┣ ┫ ┳ ┻ ╋ +Double: ╔ ═ ╗ ║ ╚ ╝ ╠ ╣ ╦ ╩ ╬ +Rounded: ╭ ╮ ╰ ╯ +Mixed: ╒ ╓ ╕ ╖ ╘ ╙ ╛ ╜ (light+double junctions) +``` + +### Blocks + +``` +Horizontal fill: █ ▉ ▊ ▋ ▌ ▍ ▎ ▏ (full → 1/8) +Vertical fill: ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ (1/8 → full) +Shade: ░ ▒ ▓ █ (25% → 100%) +Quadrants: ▖ ▗ ▘ ▝ ▞ ▟ ▙ ▛ ▜ ▚ +``` + +### Braille (sparklines, dot plots) + +``` +Range: U+2800–U+28FF (256 patterns) +Each char = 2×4 dot matrix (2 cols × 4 rows) +Smooth curves: ⠀⣀⣠⣤⣴⣶⣾⣿⣷⣶⣤⣀⠀ +``` + +### Indicators + +``` +Status: ● ○ ◐ ◑ ◒ ◓ ◌ ◉ +Arrows: ▲ ▼ ◀ ▶ ← → ↑ ↓ ↗ ↘ +Marks: ✓ ✗ ◆ ◇ ★ ☆ ⚠ ⚡ +``` + +--- + +## 9. CLI + +```bash +uframe render # emit ASCII (stdout) + .mu (file) + --ascii # ASCII only + --micron # .mu only + --ansi # ANSI colors in ASCII output + --width # override page width + --out # directory for .mu output + +uframe preview # live terminal preview + --watch # re-render on file change + +uframe check # validate / lint + +uframe deploy [dest] # render + copy to NomadNet pages dir + # default dest: ~/.nomadnetwork/storage/pages/ +``` + +--- + +## 10. Standard Component Library + +``` +use std/dashboard +use std/filebrowser +use std/board + +# Pre-built patterns: +dashboard.banner node_name uptime +dashboard.resources cpu mem gpu swap +dashboard.peers peer_list +dashboard.traffic in_data out_data + +filebrowser.tree root_path +filebrowser.listing dir_path + +board.recent count +board.compose action_path + +nav.tabs items active +nav.breadcrumb path + +chart.timeseries label values width +chart.compare items +chart.histogram bins +``` + +--- + +## 11. Future Directions + +- **Responsive reflow** — breakpoints that stack `row` children + vertically at narrow widths +- **Themes** — `.uf-theme` files defining color palettes and + border preferences shared across pages +- **Animation** — frame-by-frame for live dashboards using + terminal cursor repositioning +- **Bidirectional** — parse existing `.mu` into `.uf` for editing +- **HTML export** — for web-based Reticulum browsers (rBrowser) +- **Shebang mode** — `#!/usr/bin/env uframe --micron` for + executable dynamic NomadNet pages +- **LSP** — editor support: highlighting, completion, live preview diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..9c30227 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,46 @@ +# Micronomicon — Frontend + +React 19 + Vite + TypeScript + shadcn/ui. See the [root README](../README.md) for full project docs. + +## Dev + +```bash +npm install +npm run dev # http://localhost:5173 — proxies /api to localhost:8080 +``` + +## Build + +```bash +npm run build # output → dist/ (copied into Docker image) +``` + +## Structure + +``` +src/ + App.tsx ← routes + routes/ + DashboardView.tsx ← page table + EditorView.tsx ← split-pane editor + GraphView.tsx ← React Flow graph + components/ + dashboard/StatusBadge.tsx + editor/ + EditorPane.tsx ← CodeMirror 6 + PreviewPane.tsx ← micron output (ScrollArea) + ToolBar.tsx + shared/ + AppShell.tsx ← TooltipProvider + Toaster wrapper + NavBar.tsx + ui/ ← shadcn components (auto-generated) + lib/ + utils.ts ← cn() utility + stores/ + editorStore.ts ← Zustand: markdown, micron, dirty state + pagesStore.ts ← Zustand: pages list, fetch/delete + hooks/ + useConversion.ts ← debounced POST /api/convert + useGraph.ts ← GET /api/graph + useUnsavedGuard.ts ← beforeunload + dirty guard +``` diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..15addee --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-nova", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..0dbdaf1 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + Micronomicon + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..677c127 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,7444 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@base-ui/react": "^1.3.0", + "@codemirror/autocomplete": "^6.20.1", + "@codemirror/commands": "^6.10.3", + "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/language": "^6.12.3", + "@codemirror/search": "^6.6.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.40.0", + "@dagrejs/dagre": "^3.0.0", + "@fontsource-variable/geist": "^5.2.8", + "@tailwindcss/vite": "^4.2.2", + "@xyflow/react": "^12.10.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.7.0", + "next-themes": "^0.4.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-resizable-panels": "^4.8.0", + "react-router-dom": "^7.13.2", + "shadcn": "^4.1.1", + "sonner": "^2.0.7", + "tailwind-merge": "^3.5.0", + "tailwindcss": "^4.2.2", + "tw-animate-css": "^1.4.0", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "tslib": "^2.8.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.0", + "vite": "^8.0.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", + "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", + "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@base-ui/react": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.3.0.tgz", + "integrity": "sha512-FwpKqZbPz14AITp1CVgf4AjhKPe1OeeVKSBMdgD10zbFlj3QSWelmtCMLi2+/PFZZcIm3l87G7rwtCZJwHyXWA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@base-ui/utils": "0.2.6", + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "tabbable": "^6.4.0", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@base-ui/utils": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.6.tgz", + "integrity": "sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.6", + "@floating-ui/utils": "^0.2.11", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.1", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz", + "integrity": "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-markdown": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz", + "integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.7.1", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.3.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/markdown": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.5.tgz", + "integrity": "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.35.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/search": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", + "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.40.0", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.40.0.tgz", + "integrity": "sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@dagrejs/dagre": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-3.0.0.tgz", + "integrity": "sha512-ZzhnTy1rfuoew9Ez3EIw4L2znPGnYYhfn8vc9c4oB8iw6QAsszbiU0vRhlxWPFnmmNSFAkrYeF1PhM5m4lAN0Q==", + "license": "MIT", + "dependencies": { + "@dagrejs/graphlib": "4.0.1" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-4.0.1.tgz", + "integrity": "sha512-IvcV6FduIIAmLwnH+yun+QtV36SC7mERqa86aClNqmMN09WhmPPYU8ckHrZBozErf+UvHPWOTJYaGYiIcs0DgA==", + "license": "MIT" + }, + "node_modules/@dotenvx/dotenvx": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.59.1.tgz", + "integrity": "sha512-Qg+meC+XFxliuVSDlEPkKnaUjdaJKK6FNx/Wwl2UxhQR8pyPIuLhMavsF7ePdB9qFZUWV1jEK3ckbJir/WmF4w==", + "license": "BSD-3-Clause", + "dependencies": { + "commander": "^11.1.0", + "dotenv": "^17.2.1", + "eciesjs": "^0.4.10", + "execa": "^5.1.1", + "fdir": "^6.2.0", + "ignore": "^5.3.0", + "object-treeify": "1.1.33", + "picomatch": "^4.0.2", + "which": "^4.0.0" + }, + "bin": { + "dotenvx": "src/cli/dotenvx.js" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/isexe": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", + "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/@dotenvx/dotenvx/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@dotenvx/dotenvx/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@ecies/ciphers": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.5.tgz", + "integrity": "sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A==", + "license": "MIT", + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + }, + "peerDependencies": { + "@noble/ciphers": "^1.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@fontsource-variable/geist": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/geist/-/geist-5.2.8.tgz", + "integrity": "sha512-cJ6m9e+8MQ5dCYJsLylfZrgBh6KkG4bOLckB35Tr9J/EqdkEM6QllH5PxqP1dhTvFup+HtMRPuz9xOjxXJggxw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.12", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz", + "integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "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==", + "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==", + "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==", + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz", + "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", + "license": "MIT" + }, + "node_modules/@lezer/css": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.3.tgz", + "integrity": "sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@mswjs/interceptors": { + "version": "0.41.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", + "integrity": "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==", + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@ts-morph/common": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", + "integrity": "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==", + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.3", + "minimatch": "^10.0.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@ts-morph/common/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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-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-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-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-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/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/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "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/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "license": "MIT" + }, + "node_modules/@types/validate-npm-package-name": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/validate-npm-package-name/-/validate-npm-package-name-4.0.2.tgz", + "integrity": "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "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/react/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/@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": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz", + "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "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/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001782", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001782.tgz", + "integrity": "sha512-dZcaJLJeDMh4rELYFw1tvSn1bhZWYFOt468FcbHHxx/Z/dFidd1I6ciyFdi3iwfQCyOjqo9upF6lGQYtMiJWxw==", + "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/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/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "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/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "license": "MIT" + }, + "node_modules/cliui/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==", + "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/cliui/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "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/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "license": "MIT" + }, + "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==", + "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==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "license": "MIT" + }, + "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/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "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/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/d3-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-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-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-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-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-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/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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/eciesjs": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", + "integrity": "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==", + "license": "MIT", + "dependencies": { + "@ecies/ciphers": "^0.2.5", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.7", + "@noble/hashes": "^1.8.0" + }, + "engines": { + "bun": ">=1", + "deno": ">=2", + "node": ">=16" + } + }, + "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.329", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.329.tgz", + "integrity": "sha512-/4t+AS1l4S3ZC0Ja7PHFIWeBIxGA3QGqV8/yKsP36v7NcyUCl+bIcmw6s5zVuMIECWwBrAK/6QLzTmbJChBboQ==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "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/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.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-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.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/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/execa": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", + "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/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/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "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": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "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/fuzzysort": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz", + "integrity": "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==", + "license": "MIT" + }, + "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==", + "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==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-own-enumerable-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-own-enumerable-keys/-/get-own-enumerable-keys-1.0.0.tgz", + "integrity": "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphql": { + "version": "16.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", + "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "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/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "license": "MIT" + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/hono": { + "version": "4.12.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.9.tgz", + "integrity": "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "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/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "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/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "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-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "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-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "license": "MIT" + }, + "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-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz", + "integrity": "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-regexp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", + "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz", + "integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "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-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/msw": { + "version": "2.12.14", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.14.tgz", + "integrity": "sha512-4KXa4nVBIBjbDbd7vfQNuQ25eFxug0aropCQFoI0JdOBuJWamkT1yLVIWReFI8SiTRc+H1hKzaNk+cLk2N9rtQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.41.2", + "@open-draft/deferred-promise": "^2.2.0", + "@types/statuses": "^2.0.6", + "cookie": "^1.0.2", + "graphql": "^16.12.0", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "rettime": "^0.10.1", + "statuses": "^2.0.2", + "strict-event-emitter": "^0.5.1", + "tough-cookie": "^6.0.0", + "type-fest": "^5.2.0", + "until-async": "^3.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/object-treeify": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", + "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "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==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "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==", + "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/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", + "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/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": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-resizable-panels": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-4.8.0.tgz", + "integrity": "sha512-2uEABkewb3ky/ZgIlAUxWa1W/LjsK494fdV1QsXxst7CDRHCzo7h22tWWu3NNaBjmiuriOCt3CvhipnaYcpoIw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-router": { + "version": "7.13.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.2.tgz", + "integrity": "sha512-tX1Aee+ArlKQP+NIUd7SE6Li+CiGKwQtbS+FfRxPX6Pe4vHOo6nr9d++u5cwg+Z8K/x8tP+7qLmujDtfrAoUJA==", + "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.13.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.2.tgz", + "integrity": "sha512-aR7SUORwTqAW0JDeiWF07e9SBE9qGpByR9I8kJT5h/FrBKxPMS6TiC7rmVO+gC0q52Bx7JnjWe8Z1sR9faN4YA==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rettime": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.10.1.tgz", + "integrity": "sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "license": "MIT" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz", + "integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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/shadcn": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/shadcn/-/shadcn-4.1.1.tgz", + "integrity": "sha512-nBj+7LYC9kzV9v9QmRPpoOhfW4KctJVQejywdAt/K+K+z4RYlJOcO2a4AaF7elrRWkfCbgXeGK02liV0KB9HvQ==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/plugin-transform-typescript": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", + "@dotenvx/dotenvx": "^1.48.4", + "@modelcontextprotocol/sdk": "^1.26.0", + "@types/validate-npm-package-name": "^4.0.2", + "browserslist": "^4.26.2", + "commander": "^14.0.0", + "cosmiconfig": "^9.0.0", + "dedent": "^1.6.0", + "deepmerge": "^4.3.1", + "diff": "^8.0.2", + "execa": "^9.6.0", + "fast-glob": "^3.3.3", + "fs-extra": "^11.3.1", + "fuzzysort": "^3.1.0", + "https-proxy-agent": "^7.0.6", + "kleur": "^4.1.5", + "msw": "^2.10.4", + "node-fetch": "^3.3.2", + "open": "^11.0.0", + "ora": "^8.2.0", + "postcss": "^8.5.6", + "postcss-selector-parser": "^7.1.0", + "prompts": "^2.4.2", + "recast": "^0.23.11", + "stringify-object": "^5.0.0", + "tailwind-merge": "^3.0.1", + "ts-morph": "^26.0.0", + "tsconfig-paths": "^4.2.0", + "validate-npm-package-name": "^7.0.1", + "zod": "^3.24.1", + "zod-to-json-schema": "^3.24.6" + }, + "bin": { + "shadcn": "dist/index.js" + } + }, + "node_modules/shadcn/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "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/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-5.0.0.tgz", + "integrity": "sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==", + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-keys": "^1.0.0", + "is-obj": "^3.0.0", + "is-regexp": "^3.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/stringify-object?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, + "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/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tldts": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.27" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "license": "MIT" + }, + "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/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-morph": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-26.0.0.tgz", + "integrity": "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==", + "license": "MIT", + "dependencies": { + "@ts-morph/common": "~0.27.0", + "code-block-writer": "^13.0.3" + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "license": "0BSD" + }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", + "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "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/until-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", + "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/kettanaito" + } + }, + "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==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/validate-npm-package-name": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", + "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "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": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.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 + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "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/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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==", + "license": "MIT" + }, + "node_modules/wrap-ansi/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==", + "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/wrap-ansi/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "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==", + "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==", + "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==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "license": "MIT" + }, + "node_modules/yargs/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==", + "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/yargs/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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..d14ac41 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,55 @@ +{ + "name": "micronomicon", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@base-ui/react": "^1.3.0", + "@codemirror/autocomplete": "^6.20.1", + "@codemirror/commands": "^6.10.3", + "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/language": "^6.12.3", + "@codemirror/search": "^6.6.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.40.0", + "@dagrejs/dagre": "^3.0.0", + "@fontsource-variable/geist": "^5.2.8", + "@tailwindcss/vite": "^4.2.2", + "@xyflow/react": "^12.10.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.7.0", + "next-themes": "^0.4.6", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-resizable-panels": "^4.8.0", + "react-router-dom": "^7.13.2", + "shadcn": "^4.1.1", + "sonner": "^2.0.7", + "tailwind-merge": "^3.5.0", + "tailwindcss": "^4.2.2", + "tw-animate-css": "^1.4.0", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@types/node": "^24.12.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "tslib": "^2.8.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.0", + "vite": "^8.0.1" + } +} diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/icons.svg b/frontend/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/frontend/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..af23375 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,18 @@ +import { Routes, Route } from "react-router-dom"; +import AppShell from "./components/shared/AppShell"; +import DashboardView from "./routes/DashboardView"; +import EditorView from "./routes/EditorView"; +import GraphView from "./routes/GraphView"; + +export default function App() { + return ( + + + } /> + } /> + } /> + } /> + + + ); +} diff --git a/frontend/src/components/dashboard/StatusBadge.tsx b/frontend/src/components/dashboard/StatusBadge.tsx new file mode 100644 index 0000000..6f85b8f --- /dev/null +++ b/frontend/src/components/dashboard/StatusBadge.tsx @@ -0,0 +1,12 @@ +import { Badge } from "@/components/ui/badge"; + +interface Props { + published: boolean; + hasSource: boolean; +} + +export default function StatusBadge({ published, hasSource }: Props) { + if (published && hasSource) return Published; + if (!published && hasSource) return Draft; + return Orphan; +} diff --git a/frontend/src/components/editor/BacklinkIndicator.tsx b/frontend/src/components/editor/BacklinkIndicator.tsx new file mode 100644 index 0000000..1e3c4a3 --- /dev/null +++ b/frontend/src/components/editor/BacklinkIndicator.tsx @@ -0,0 +1,44 @@ +import { Link } from "react-router-dom"; +import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover"; +import { Button } from "@/components/ui/button"; +import type { BacklinkPage } from "@/hooks/useBacklinks"; + +interface Props { + backlinks: BacklinkPage[]; +} + +export default function BacklinkIndicator({ backlinks }: Props) { + if (backlinks.length === 0) return null; + + return ( + + + + + +

+ Pages linking here +

+
    + {backlinks.map((page) => ( +
  • + + {page.title ?? page.name} + {page.title && ( + + ({page.name}) + + )} + +
  • + ))} +
+
+
+ ); +} diff --git a/frontend/src/components/editor/EditorPane.tsx b/frontend/src/components/editor/EditorPane.tsx new file mode 100644 index 0000000..013866b --- /dev/null +++ b/frontend/src/components/editor/EditorPane.tsx @@ -0,0 +1,61 @@ +import { useEffect, useRef } from "react"; +import { EditorView, keymap } from "@codemirror/view"; +import { EditorState } from "@codemirror/state"; +import type { Extension } from "@codemirror/state"; +import { defaultKeymap, history, historyKeymap } from "@codemirror/commands"; +import { searchKeymap } from "@codemirror/search"; +import { oneDark } from "./oneDarkTheme"; + +interface Props { + value: string; + onChange: (value: string) => void; + extensions?: Extension[]; +} + +export default function EditorPane({ value, onChange, extensions = [] }: Props) { + const containerRef = useRef(null); + const viewRef = useRef(null); + const onChangeRef = useRef(onChange); + onChangeRef.current = onChange; + + useEffect(() => { + if (!containerRef.current) return; + + const state = EditorState.create({ + doc: value, + extensions: [ + history(), + keymap.of([...defaultKeymap, ...historyKeymap, ...searchKeymap]), + oneDark, + EditorView.updateListener.of((update) => { + if (update.docChanged) { + onChangeRef.current(update.state.doc.toString()); + } + }), + EditorView.theme({ + "&": { height: "100%", fontSize: "14px" }, + ".cm-scroller": { overflow: "auto" }, + ".cm-content": { fontFamily: "monospace", padding: "16px" }, + }), + ...extensions, + ], + }); + + const view = new EditorView({ state, parent: containerRef.current }); + viewRef.current = view; + + return () => view.destroy(); + }, []); // Only mount once + + // Sync external value changes (e.g. loading a page) + useEffect(() => { + const view = viewRef.current; + if (view && view.state.doc.toString() !== value) { + view.dispatch({ + changes: { from: 0, to: view.state.doc.length, insert: value }, + }); + } + }, [value]); + + return
; +} diff --git a/frontend/src/components/editor/PreviewPane.tsx b/frontend/src/components/editor/PreviewPane.tsx new file mode 100644 index 0000000..de6feff --- /dev/null +++ b/frontend/src/components/editor/PreviewPane.tsx @@ -0,0 +1,88 @@ +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Button } from "@/components/ui/button"; +import { useEditorStore } from "@/stores/editorStore"; +import { renderMicron } from "./micronRenderer"; +import { cn } from "@/lib/utils"; + +type PreviewMode = "ascii" | "micron" | "raw"; + +export default function PreviewPane() { + const previewMode = useEditorStore((s) => s.previewMode); + const setPreviewMode = useEditorStore((s) => s.setPreviewMode); + const compiledAscii = useEditorStore((s) => s.compiledAscii); + const compiledMicron = useEditorStore((s) => s.compiledMicron); + const isCompiling = useEditorStore((s) => s.isCompiling); + const compileError = useEditorStore((s) => s.compileError); + + const tabs: { value: PreviewMode; label: string }[] = [ + { value: "ascii", label: "ASCII" }, + { value: "micron", label: "Micron" }, + { value: "raw", label: "Raw" }, + ]; + + return ( +
+
+ + Preview + {isCompiling && ( + + compiling… + + )} + {compileError && ( + + ✗ error + + )} + +
+ {tabs.map((tab) => ( + + ))} +
+
+ + {previewMode === "ascii" ? ( +
+            {compiledAscii || (
+              
+                ASCII preview will appear here…
+              
+            )}
+          
+ ) : previewMode === "micron" ? ( + compiledMicron ? ( +
+ ) : ( +
+ + Micron preview will appear here… + +
+ ) + ) : ( +
+            {compiledMicron || "Raw Micron output will appear here…"}
+          
+ )} + +
+ ); +} diff --git a/frontend/src/components/editor/ToolBar.tsx b/frontend/src/components/editor/ToolBar.tsx new file mode 100644 index 0000000..c79332c --- /dev/null +++ b/frontend/src/components/editor/ToolBar.tsx @@ -0,0 +1,56 @@ +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { useEditorStore } from "@/stores/editorStore"; +import { useBacklinks } from "@/hooks/useBacklinks"; +import BacklinkIndicator from "@/components/editor/BacklinkIndicator"; + +interface Props { + pageName: string; + onNameChange?: (name: string) => void; + onSaveDraft: () => void; + onPublish: () => void; + saving: boolean; + isDirty: boolean; +} + +export default function ToolBar({ + pageName, + onNameChange, + onSaveDraft, + onPublish, + saving, + isDirty, +}: Props) { + const currentPage = useEditorStore((s) => s.currentPage); + const backlinks = useBacklinks(currentPage?.name); + + return ( +
+ {onNameChange ? ( + onNameChange(e.target.value)} + placeholder="page-name" + className="font-mono w-48 h-8 text-sm" + /> + ) : ( + {pageName} + )} + +
+ + + + {isDirty && ( + Unsaved + )} + + + +
+ ); +} diff --git a/frontend/src/components/editor/micronHighlight.ts b/frontend/src/components/editor/micronHighlight.ts new file mode 100644 index 0000000..9e2455a --- /dev/null +++ b/frontend/src/components/editor/micronHighlight.ts @@ -0,0 +1,57 @@ +import { StreamLanguage, HighlightStyle, syntaxHighlighting } from "@codemirror/language"; +import { tags } from "@lezer/highlight"; + +const micronLanguage = StreamLanguage.define({ + token(stream) { + if (stream.sol()) { + // Depth-4+ indent (before >>> so ">>>> " doesn't match heading3) + if (stream.match(/>>>>/)) { stream.skipToEnd(); return "keyword"; } + // Headings — longest prefix first + if (stream.match(/>>>/)) { stream.skipToEnd(); return "heading3"; } + if (stream.match(/>>/)) { stream.skipToEnd(); return "heading2"; } + if (stream.match(/>/)) { stream.skipToEnd(); return "heading1"; } + // Dividers: line starting with - followed by a non-space, non-dash char + if (stream.match(/-[^\s\-]/)) { stream.skipToEnd(); return "contentSeparator"; } + // Comment lines + if (stream.match(/#/)) { stream.skipToEnd(); return "lineComment"; } + // Standalone depth-reset "<" + if (stream.string.trim() === "<") { stream.next(); return "meta"; } + } + + // Backtick-based format tags: `! `* `_ `` `F `f `B `b `c `r `l `a `= `< + if (stream.match(/`[!*_`FfBbCcRrLlAa=<]/)) return "meta"; + + // Hex color values (exactly 3 hex digits) — appear right after `F or `B tags + if (stream.match(/[0-9a-fA-F]{3}(?![0-9a-fA-F])/)) return "number"; + + // Links [label`url] — consume the whole bracket expression + if (stream.match(/\[[^\]]*\]/)) return "link"; + + // Form elements etc. + if (stream.match(/<[^>]+>/)) return "string"; + + stream.next(); + return null; + }, + startState: () => ({}), + copyState: (s) => ({ ...s }), + blankLine: () => {}, + languageData: {}, +}); + +const micronStyle = HighlightStyle.define([ + { tag: tags.heading1, color: "#7ee8a2", fontWeight: "bold" }, + { tag: tags.heading2, color: "#70c4e8", fontWeight: "bold" }, + { tag: tags.heading3, color: "#a8c4e8", fontWeight: "bold" }, + { tag: tags.keyword, color: "#c9d1d9", fontStyle: "italic" }, // depth-4+ indent + { tag: tags.contentSeparator, color: "#484f58", fontStyle: "italic" }, + { tag: tags.lineComment, color: "#484f58", fontStyle: "italic" }, // # comments + { tag: tags.meta, color: "#d2a8ff" }, // backtick format codes + { tag: tags.number, color: "#f8d4a8" }, // hex color values + { tag: tags.link, color: "#7dc4e4", textDecoration: "underline" }, + { tag: tags.string, color: "#d4a8f8" }, // form elements +]); + +export function micronHighlight() { + return [micronLanguage, syntaxHighlighting(micronStyle)]; +} diff --git a/frontend/src/components/editor/micronRenderer.ts b/frontend/src/components/editor/micronRenderer.ts new file mode 100644 index 0000000..9f8eec0 --- /dev/null +++ b/frontend/src/components/editor/micronRenderer.ts @@ -0,0 +1,157 @@ +/** + * Micron markup → HTML renderer for the editor preview pane. + * Spec: https://github.com/fr33n0w/micron-composer + */ + +function escapeHtml(text: string): string { + return text + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); +} + +/** Render inline Micron formatting codes within a line of text. */ +function renderInline(raw: string): string { + let out = ""; + let i = 0; + const openTags: string[] = []; + + const closeAll = () => { + while (openTags.length) out += openTags.pop()!; + }; + + while (i < raw.length) { + // Backtick formatting codes + if (raw[i] === "`") { + const code = raw[i + 1]; + if (code === "!") { + out += ""; openTags.push(""); i += 2; continue; + } else if (code === "*") { + out += ""; openTags.push(""); i += 2; continue; + } else if (code === "_") { + out += ""; openTags.push(""); i += 2; continue; + } else if (code === "`") { + closeAll(); i += 2; continue; + } else if (code === "f" || code === "b") { + out += ""; i += 2; continue; + } else if (code === "a") { + out += ""; i += 2; continue; + } else if (code === "F") { + // Foreground color — 3-digit hex only per spec + const hexMatch = raw.slice(i + 2).match(/^([0-9a-fA-F]{3})(?![0-9a-fA-F])/); + if (hexMatch) { + const [r, g, b] = hexMatch[1].split(""); + const hex = r + r + g + g + b + b; + out += ``; + openTags.push(""); + i += 2 + hexMatch[1].length; + continue; + } + } else if (code === "B") { + // Background color — 3-digit hex only per spec + const hexMatch = raw.slice(i + 2).match(/^([0-9a-fA-F]{3})(?![0-9a-fA-F])/); + if (hexMatch) { + const [r, g, b] = hexMatch[1].split(""); + const hex = r + r + g + g + b + b; + out += ``; + openTags.push(""); + i += 2 + hexMatch[1].length; + continue; + } + } else if (code === "c") { + out += ``; + openTags.push(""); i += 2; continue; + } else if (code === "r") { + out += ``; + openTags.push(""); i += 2; continue; + } else if (code === "l") { + out += ``; + openTags.push(""); i += 2; continue; + } + } + + // Links: [label`slug] or [label`slug.mu] + if (raw[i] === "[") { + const close = raw.indexOf("]", i); + if (close !== -1) { + const inner = raw.slice(i + 1, close); + const backtick = inner.indexOf("`"); + if (backtick !== -1) { + const label = escapeHtml(inner.slice(0, backtick)); + const slug = escapeHtml(inner.slice(backtick + 1).replace(/\.mu$/, "")); + out += `${label}`; + i = close + 1; + continue; + } + } + } + + out += escapeHtml(raw[i]); + i++; + } + + closeAll(); + return out; +} + +/** Render a form element line as a styled badge. */ +function renderForm(line: string): string { + const inner = escapeHtml(line); + return `${inner}`; +} + +export function renderMicron(source: string): string { + const lines = source.split("\n"); + const htmlLines: string[] = []; + let literalMode = false; + + for (const line of lines) { + // Toggle literal mode on standalone `= line + if (line.trimEnd() === "`=") { + literalMode = !literalMode; + continue; + } + + // In literal mode — render verbatim + if (literalMode) { + htmlLines.push(`
${escapeHtml(line)}
`); + continue; + } + + // Comment lines — hidden in output + if (line.startsWith("#")) continue; + + // All output uses inline spans — the parent container has white-space:pre + // so newlines come from the \n join at the end. + + // Depth-4+ indent (before >>> check) + if (line.startsWith(">>>>")) { + htmlLines.push(`${renderInline(line.slice(4))}`); + // Headings + } else if (line.startsWith(">>>")) { + htmlLines.push(`${renderInline(line.slice(3))}`); + } else if (line.startsWith(">>")) { + htmlLines.push(`${renderInline(line.slice(2))}`); + } else if (line.startsWith(">")) { + htmlLines.push(`${renderInline(line.slice(1))}`); + // Dividers: - followed by a non-space, non-dash character + } else if (/^-[^\s-]/.test(line)) { + const char = line[1]; + htmlLines.push(`${char.repeat(40)}`); + // Standalone depth-reset "<" + } else if (line.trim() === "<") { + htmlLines.push(`↩ depth reset`); + // Empty line + } else if (line.trim() === "") { + htmlLines.push(""); + // Form elements on their own line + } else if (/^`?<[^>]+>$/.test(line)) { + htmlLines.push(renderForm(line)); + } else { + htmlLines.push(renderInline(line)); + } + } + + return htmlLines.join("\n"); +} diff --git a/frontend/src/components/editor/oneDarkTheme.ts b/frontend/src/components/editor/oneDarkTheme.ts new file mode 100644 index 0000000..570fdce --- /dev/null +++ b/frontend/src/components/editor/oneDarkTheme.ts @@ -0,0 +1,28 @@ +import { EditorView } from "@codemirror/view"; + +export const oneDark = EditorView.theme( + { + "&": { + backgroundColor: "#0d1117", + color: "#c9d1d9", + }, + ".cm-cursor": { + borderLeftColor: "#c9d1d9", + }, + ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { + backgroundColor: "#264f78", + }, + ".cm-activeLine": { + backgroundColor: "#161b2266", + }, + ".cm-gutters": { + backgroundColor: "#0d1117", + color: "#484f58", + borderRight: "1px solid #21262d", + }, + ".cm-activeLineGutter": { + backgroundColor: "#161b2266", + }, + }, + { dark: true } +); diff --git a/frontend/src/components/editor/slashCommands.ts b/frontend/src/components/editor/slashCommands.ts new file mode 100644 index 0000000..4a13fcf --- /dev/null +++ b/frontend/src/components/editor/slashCommands.ts @@ -0,0 +1,84 @@ +import { snippet } from "@codemirror/autocomplete"; +import type { Completion, CompletionContext, CompletionResult } from "@codemirror/autocomplete"; +import type { EditorView } from "@codemirror/view"; + +interface SlashEntry { + label: string; + detail: string; + section: string; + apply: Completion["apply"]; +} + +// Insert text, replacing from the "/" character (from-1) through the cursor +function insert(text: string): Completion["apply"] { + return (view: EditorView, _completion: Completion, from: number, to: number) => { + view.dispatch({ changes: { from: from - 1, to, insert: text } }); + }; +} + +// Wrap snippet() to also replace the preceding "/" character +function slashSnippet(template: string): Completion["apply"] { + const snip = snippet(template); + return (view: EditorView, completion: Completion, from: number, to: number) => { + snip(view, completion, from - 1, to - 1); + }; +} + +const COMMANDS: SlashEntry[] = [ + // Headings + { label: "H1", detail: ">...", section: "Heading", apply: slashSnippet(">\${text}") }, + { label: "H2", detail: ">>...", section: "Heading", apply: slashSnippet(">>\${text}") }, + { label: "H3", detail: ">>>...", section: "Heading", apply: slashSnippet(">>>\${text}") }, + + // Text formatting + { label: "Bold", detail: "`!..`!", section: "Format", apply: slashSnippet("`!\${text}`!") }, + { label: "Italic", detail: "`*..`*", section: "Format", apply: slashSnippet("`*\${text}`*") }, + { label: "Underline", detail: "`_..`_", section: "Format", apply: slashSnippet("`_\${text}`_") }, + { label: "Reset", detail: "``", section: "Format", apply: insert("``") }, + { label: "Literal", detail: "`=...`=", section: "Format", apply: slashSnippet("`=\n\${content}\n`=") }, + + // Alignment + { label: "Center", detail: "`c..`a", section: "Align", apply: slashSnippet("`c\${text}`a") }, + { label: "Right", detail: "`r..`a", section: "Align", apply: slashSnippet("`r\${text}`a") }, + { label: "Left", detail: "`l..`a", section: "Align", apply: slashSnippet("`l\${text}`a") }, + + // Color (3-digit hex) + { label: "Color", detail: "`Fhex..`f", section: "Color", apply: slashSnippet("`F\${hex}\${text}`f") }, + { label: "BgColor", detail: "`Bhex..`b", section: "Color", apply: slashSnippet("`B\${hex}\${text}`b") }, + + // Links + { label: "Link", detail: "[label`page]", section: "Link", apply: slashSnippet("[\${label}`\${page}]") }, + + // Dividers + { label: "Divider ─", detail: "-─", section: "Divider", apply: insert("-─") }, + { label: "Divider ━", detail: "-━", section: "Divider", apply: insert("-━") }, + { label: "Divider ═", detail: "-═", section: "Divider", apply: insert("-═") }, + { label: "Divider ★", detail: "-★", section: "Divider", apply: insert("-★") }, + + // Forms — pipe separators per micron-composer spec + { label: "Field", detail: "", section: "Form", apply: slashSnippet("<\${name}`\${default}>") }, + { label: "Password", detail: "", section: "Form", apply: slashSnippet("") }, + { label: "Checkbox", detail: "", section: "Form", apply: slashSnippet("") }, + { label: "Checked", detail: "", section: "Form", apply: slashSnippet("") }, + { label: "Radio", detail: "<^|grp|val`label>", section: "Form", apply: slashSnippet("<^\${group}|\${value}`\${label}>") }, + + // Depth + { label: "Reset depth", detail: "<", section: "Depth", apply: insert("<\n") }, +]; + +export function slashCommandSource(ctx: CompletionContext): CompletionResult | null { + const match = ctx.matchBefore(/\/\w*/); + if (!match || (match.from === match.to && !ctx.explicit)) return null; + return { + // Start after "/" so the filter text doesn't include "/" (which would block all matches) + from: match.from + 1, + filter: true, + options: COMMANDS.map((cmd) => ({ + label: cmd.label, + detail: cmd.detail, + section: cmd.section, + apply: cmd.apply, + boost: 99, + })), + }; +} diff --git a/frontend/src/components/editor/uframeCommands.ts b/frontend/src/components/editor/uframeCommands.ts new file mode 100644 index 0000000..4f5889b --- /dev/null +++ b/frontend/src/components/editor/uframeCommands.ts @@ -0,0 +1,195 @@ +import { snippet } from "@codemirror/autocomplete"; +import type { + Completion, + CompletionContext, + CompletionResult, +} from "@codemirror/autocomplete"; +import type { EditorView } from "@codemirror/view"; + +interface CmdEntry { + label: string; + detail: string; + section: string; + apply: Completion["apply"]; +} + +function insert(text: string): Completion["apply"] { + return (view: EditorView, _c: Completion, from: number, to: number) => { + view.dispatch({ changes: { from: from - 1, to, insert: text } }); + }; +} + +function slashSnippet(template: string): Completion["apply"] { + const snip = snippet(template); + return (view: EditorView, c: Completion, from: number, to: number) => { + snip(view, c, from - 1, to - 1); + }; +} + +const COMMANDS: CmdEntry[] = [ + // Layout + { + label: "page", + detail: 'page "Title" 64', + section: "Layout", + apply: slashSnippet('page "${title}" ${width:64}'), + }, + { + label: "box", + detail: 'box light "Title"', + section: "Layout", + apply: slashSnippet('box ${weight:light} "${title}"'), + }, + { + label: "row", + detail: "row [gap]", + section: "Layout", + apply: slashSnippet("row ${gap:2}"), + }, + { + label: "col", + detail: "col [width]", + section: "Layout", + apply: slashSnippet("col ${width}"), + }, + { + label: "spacer", + detail: "spacer [lines]", + section: "Layout", + apply: insert("spacer"), + }, + { + label: "pad", + detail: "pad t r b l", + section: "Layout", + apply: slashSnippet("pad ${top:1} ${right:1} ${bottom:1} ${left:1}"), + }, + + // Content + { + label: "heading", + detail: 'heading 1 "Text"', + section: "Content", + apply: slashSnippet('heading ${level:1} "${text}"'), + }, + { + label: "text", + detail: 'text "Content"', + section: "Content", + apply: slashSnippet('text "${content}"'), + }, + { + label: "label", + detail: 'label "Key" "Value"', + section: "Content", + apply: slashSnippet('label "${key}" "${value}"'), + }, + { + label: "divider", + detail: "divider heavy", + section: "Content", + apply: slashSnippet("divider ${style:light}"), + }, + { + label: "link", + detail: 'link "Text" "/path.mu"', + section: "Content", + apply: slashSnippet('link "${display}" "${dest}"'), + }, + { + label: "list", + detail: "list bullet", + section: "Content", + apply: slashSnippet("list ${style:bullet}\n item \"${entry}\""), + }, + + // Data Viz + { + label: "gauge", + detail: "gauge label val max width", + section: "Data", + apply: slashSnippet( + 'gauge "${label}" ${value} ${max:100} ${width:28} warn=${warn:75} crit=${crit:90}', + ), + }, + { + label: "sparkline", + detail: "sparkline label values width", + section: "Data", + apply: slashSnippet('sparkline "${label}" "${values}" ${width:20}'), + }, + { + label: "status", + detail: "status label state", + section: "Data", + apply: slashSnippet('status "${label}" ${state:online}'), + }, + + // Style + { + label: "align", + detail: "align center", + section: "Style", + apply: slashSnippet("align ${align:center}"), + }, + { + label: "color", + detail: "color hex", + section: "Style", + apply: slashSnippet("color ${hex}"), + }, + { + label: "bold", + detail: "bold", + section: "Style", + apply: insert("bold"), + }, + + // Templates + { + label: "dashboard", + detail: "Full dashboard template", + section: "Template", + apply: insert( + `page "Dashboard" 64 + box double "Node Status" + align center + text "Reticulum Network Node" + + spacer + + heading 1 "Resources" + + gauge "CPU" 0 100 28 warn=75 crit=90 + gauge "MEM" 0 100 28 warn=80 crit=95 + + spacer + + heading 2 "Network" + + status "Relay East" online + status "Bridge South" online + + divider heavy + link "Home" "/page/index.mu"`, + ), + }, +]; + +export function uframeCommandSource( + ctx: CompletionContext, +): CompletionResult | null { + const match = ctx.matchBefore(/\/\w*/); + if (!match || (match.from === match.to && !ctx.explicit)) return null; + return { + from: match.from + 1, + filter: true, + options: COMMANDS.map((cmd) => ({ + label: cmd.label, + detail: cmd.detail, + section: cmd.section, + apply: cmd.apply, + boost: 99, + })), + }; +} diff --git a/frontend/src/components/editor/uframeHighlight.ts b/frontend/src/components/editor/uframeHighlight.ts new file mode 100644 index 0000000..fc14fb0 --- /dev/null +++ b/frontend/src/components/editor/uframeHighlight.ts @@ -0,0 +1,101 @@ +import { + StreamLanguage, + HighlightStyle, + syntaxHighlighting, +} from "@codemirror/language"; +import { tags } from "@lezer/highlight"; + +/** + * CodeMirror 6 syntax highlighting for the µFrame .uf DSL. + * + * Keywords: page, box, row, col, spacer, pad, heading, text, label, + * divider, link, list, item, gauge, sparkline, status, + * table, columns, form, field, radio, checkbox, button, + * source, let, if, elif, else, for, align, color, bg, + * bold, italic, underline, cache, state, on_submit + */ + +const KEYWORDS = new Set([ + "page", "box", "row", "col", "spacer", "pad", + "heading", "text", "label", "divider", "link", + "list", "item", "gauge", "sparkline", "status", + "table", "columns", "form", "field", "radio", + "checkbox", "button", "source", "let", "if", + "elif", "else", "for", "align", "color", "bg", + "bold", "italic", "underline", "cache", "state", + "on_submit", "meter", "bar_h", "bar_v", "bar", + "heatmap", "component", "use", +]); + +const WEIGHT_VALS = new Set([ + "light", "heavy", "double", "rounded", + "bullet", "dash", "number", "arrow", + "left", "center", "right", + "online", "offline", "degraded", "unknown", +]); + +const uframeLanguage = StreamLanguage.define({ + token(stream) { + // Comments + if (stream.sol() && stream.match(/\s*#/)) { + stream.skipToEnd(); + return "lineComment"; + } + + // Skip whitespace + if (stream.eatSpace()) return null; + + // Quoted strings + if (stream.match(/"/)) { + while (!stream.eol()) { + if (stream.next() === '"') break; + } + return "string"; + } + + // @modifier{} syntax + if (stream.match(/@\w+/)) return "keyword"; + + // $variable references + if (stream.match(/\$[\w.]+/)) return "variableName"; + + // Numbers (integers, floats, hex colors) + if (stream.match(/\b\d+(\.\d+)?\b/)) return "number"; + + // warn=N, crit=N parameters + if (stream.match(/\b(warn|crit)=/)) return "attributeName"; + + // Pipe separator for inline lists + if (stream.match(/\|/)) return "punctuation"; + + // Keywords and values + if (stream.match(/\b\w+\b/)) { + const word = stream.current(); + if (KEYWORDS.has(word)) return "keyword"; + if (WEIGHT_VALS.has(word)) return "atom"; + return null; + } + + stream.next(); + return null; + }, + startState: () => ({}), + copyState: (s) => ({ ...s }), + blankLine: () => {}, + languageData: {}, +}); + +const uframeStyle = HighlightStyle.define([ + { tag: tags.keyword, color: "#c792ea", fontWeight: "bold" }, + { tag: tags.string, color: "#c3e88d" }, + { tag: tags.lineComment, color: "#546e7a", fontStyle: "italic" }, + { tag: tags.variableName, color: "#f78c6c" }, + { tag: tags.number, color: "#f78c6c" }, + { tag: tags.atom, color: "#89ddff" }, + { tag: tags.attributeName, color: "#ffcb6b" }, + { tag: tags.punctuation, color: "#89ddff" }, +]); + +export function uframeHighlight() { + return [uframeLanguage, syntaxHighlighting(uframeStyle)]; +} diff --git a/frontend/src/components/editor/wikiLinkCompletion.ts b/frontend/src/components/editor/wikiLinkCompletion.ts new file mode 100644 index 0000000..c10cb9d --- /dev/null +++ b/frontend/src/components/editor/wikiLinkCompletion.ts @@ -0,0 +1,23 @@ +import type { CompletionContext, CompletionResult, Completion } from "@codemirror/autocomplete"; +import type { MutableRefObject } from "react"; +import type { PageMeta } from "@/stores/editorStore"; + +export function wikiLinkSource(pagesRef: MutableRefObject) { + return (context: CompletionContext): CompletionResult | null => { + const match = context.matchBefore(/\[\[[\w-]*/); + if (!match || (match.from === match.to && !context.explicit)) return null; + + const options: Completion[] = pagesRef.current.map((page) => ({ + label: page.title ?? page.name, + detail: page.name, + apply: (view, _completion, from, to) => { + const title = page.title ?? page.name; + view.dispatch({ + changes: { from, to, insert: `[${title}\`${page.name}]` }, + }); + }, + })); + + return { from: match.from, options, filter: true }; + }; +} diff --git a/frontend/src/components/shared/AppShell.tsx b/frontend/src/components/shared/AppShell.tsx new file mode 100644 index 0000000..03bc226 --- /dev/null +++ b/frontend/src/components/shared/AppShell.tsx @@ -0,0 +1,16 @@ +import type { ReactNode } from "react"; +import NavBar from "./NavBar"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { Toaster } from "@/components/ui/sonner"; + +export default function AppShell({ children }: { children: ReactNode }) { + return ( + +
+ +
{children}
+
+ +
+ ); +} diff --git a/frontend/src/components/shared/NavBar.tsx b/frontend/src/components/shared/NavBar.tsx new file mode 100644 index 0000000..c228c28 --- /dev/null +++ b/frontend/src/components/shared/NavBar.tsx @@ -0,0 +1,88 @@ +import { useState } from "react"; +import { NavLink } from "react-router-dom"; +import { toast } from "sonner"; +import { RotateCcw } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; + +const navLink = ({ isActive }: { isActive: boolean }) => + cn( + "px-3 py-1.5 text-sm rounded-md transition-colors", + isActive + ? "bg-accent text-accent-foreground font-medium" + : "text-muted-foreground hover:text-foreground hover:bg-accent" + ); + +export default function NavBar() { + const [restartOpen, setRestartOpen] = useState(false); + const [restarting, setRestarting] = useState(false); + + const handleRestart = async () => { + setRestarting(true); + try { + const res = await fetch("/api/restart", { method: "POST" }); + if (!res.ok) throw new Error(await res.text()); + toast.success("NomadNet restarted"); + } catch (e) { + toast.error(`Restart failed: ${e}`); + } finally { + setRestarting(false); + setRestartOpen(false); + } + }; + + return ( + + ); +} diff --git a/frontend/src/components/ui/alert-dialog.tsx b/frontend/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..0ee2c5f --- /dev/null +++ b/frontend/src/components/ui/alert-dialog.tsx @@ -0,0 +1,187 @@ +"use client" + +import * as React from "react" +import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) { + return +} + +function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) { + return ( + + ) +} + +function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: AlertDialogPrimitive.Backdrop.Props) { + return ( + + ) +} + +function AlertDialogContent({ + className, + size = "default", + ...props +}: AlertDialogPrimitive.Popup.Props & { + size?: "default" | "sm" +}) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogMedia({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( +