diff --git a/.vscode/extensions.json b/.vscode/extensions.json
deleted file mode 100644
index 24d7cc6..0000000
--- a/.vscode/extensions.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
-}
diff --git a/README.md b/README.md
index 102e366..8e619c4 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1 @@
-# Tauri + React + Typescript
-
-This template should help get you started developing with Tauri, React and Typescript in Vite.
-
-## Recommended IDE Setup
-
-- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
+# Writer
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..3294ae8
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,181 @@
+# Architecture
+
+## Foundational Assumptions
+
+- **Canonical storage = OS locations (folders)** + **sidecar SQLite index + JSON settings**.
+- Markdown parsing/rendering is **authoritative in Rust** (single source of truth), using a spec-complete CommonMark+GFM engine (recommended: **Comrak**). ([Crates.io][comrak])
+- Frontend uses **Elm architecture** with `Model/Msg/update/Cmd/Sub`.
+- Rust communicates with the frontend via **Tauri commands + events**. ([Tauri][tauri-calling-rust])
+
+## Constraints
+
+1. **React + Tauri** desktop app.
+2. **Hand-made Elm architecture** for state management (single source of truth `Model`, message-driven `update`, explicit `Cmd` effects, `Subscriptions`).
+3. Storage must follow **three patterns**:
+ - **Filesystem as canonical store**
+ - **Sidecar metadata**
+ - **Sync as transport**
+4. Must use **OS "Locations"**: user picks folders via native dialogs; app persists access, and treats those folders as roots for documents.
+5. Security posture: Tauri **capabilities/permissions** and filesystem scoping, not "unrestricted arbitrary fs" by default. ([Tauri][tauri-capabilities])
+
+## Workspace Layout
+
+- `src/` (React UI)
+- `crates/core/` (`writer-core` domain types, markdown API, storage API)
+- `crates/markdown/` (`writer-md` markdown engine wrapper around Comrak)
+- `crates/store/` (`writer-store` SQLite + FTS)
+- `src-tauri/` (`writer` Tauri app + command handlers)
+- `fixtures/markdown/` — input markdown + expected HTML + expected outline metadata
+- `fixtures/render/` — XSS / raw HTML testcases
+
+## Data Model
+
+### Canonical content (user-controlled)
+
+- All user documents are **plain-text files** stored *in the user's chosen locations* (e.g., `~/Documents/Writing`, `~/Library/Mobile Documents/...` for iCloud Drive, Dropbox folder, etc.).
+- Supported file types (MVP):
+
+ - `.md` (Markdown)
+ - `.txt` (plain text)
+ - optional: `.mdx` later (non-MVP)
+
+The app **never requires** content to live in an app-private container.
+
+### Sidecar metadata (app-controlled)
+
+All app-owned derived data lives in the app's application data directory (e.g., `AppData`/`Application Support` equivalent), not inside user content roots.
+
+- `app.db` (SQLite)
+
+ - Search index via **FTS** (FTS5 recommended)
+ - Document catalog (path, file hash/mtime, size, encoding)
+ - Tag index (derived from frontmatter or inline syntax)
+ - "Recent documents", pin list, writing sessions stats
+- `settings.json` (small, human-readable)
+
+ - UI preferences, editor settings, enabled locations list
+- `workspace.json`
+
+ - Window layout, last open doc, sidebar state, etc.
+
+> Tauri provides an official **Store plugin** for persisting small state to a file (async), which is appropriate for `settings/workspace` class data. ([Tauri][tauri-store])
+
+## Frontend: Elm Loop in React
+
+Define:
+
+- `Model` (single immutable state tree)
+- `Msg` (all possible events - TS enum)
+- `update(model, msg) -> [model, Cmd[]]`
+- `Cmd` (effects): invokes Rust commands, timers, file watchers subscription changes
+- `subscriptions(model) -> Sub[]`:
+ - file watcher events
+ - debounced autosave ticks
+ - OS focus/blur, window events
+
+**Rule:** UI components are pure views of `Model`. They may *dispatch* `Msg`, but they do not perform I/O.
+
+## Core: Rust "Ports" Layer
+
+All filesystem and indexing operations happen in Rust commands (Tauri backend), because:
+
+- Tauri's JS fs APIs are scoped and safe, but a Rust core gives you stronger control over:
+ - atomic writes
+ - canonicalization
+ - watcher integration
+ - cross-platform path correctness
+- Tauri's security model centers on **capabilities and permissioned commands**. ([Tauri][tauri-capabilities])
+
+### Core Commands
+
+- `location_add_via_dialog() -> LocationDescriptor`
+- `location_remove(LocationId)`
+- `doc_list(LocationId, filter/sort) -> DocMeta[]`
+- `doc_open(DocId) -> { text, meta }`
+- `doc_save(DocId, text, policy) -> SaveResult`
+- `doc_rename/move/delete`
+- `index_rebuild(LocationId)`
+- `search(query, options) -> SearchHit[]`
+- `watch_enable(LocationId)` / `watch_disable(LocationId)` (or auto from subscriptions)
+
+### Command Contract
+
+- Standard response envelope:
+ - `Ok(T)` / `Err(AppError { code, message, context })`
+- Error codes: `NotFound`, `PermissionDenied`, `InvalidPath`, `Io`, `Parse`, `Index`, `Conflict`
+
+## Storage Behaviors
+
+### Writes must be atomic
+
+- Save pipeline:
+
+ 1. write to temp file in same directory (or safe temp under location root)
+ 2. fsync as available
+ 3. rename/replace original
+- Update index after successful rename/replace.
+- If provider conflicts occur (e.g., Dropbox "conflicted copy"), treat as a new file and surface a conflict UI.
+
+### File identity & change detection
+
+Store in SQLite:
+
+- `path`
+- `location_id`
+- `mtime`, `size`
+- `content_hash` (fast hash; compute on open/save and optionally on watcher events)
+- `doc_id` stable key = `(location_id, normalized_relative_path)`; if path changes, doc_id changes unless you implement inode/file-id based identity.
+
+### Encoding & line endings
+
+- Assume UTF-8 by default; detect BOM and handle losslessly.
+- Preserve line endings on save unless user opts into normalization.
+
+## Indexing & Search
+
+### Index policy
+
+- SQLite FTS index is **derived**:
+
+ - It can always be rebuilt.
+ - It must never be required for opening/editing a file.
+- Index update triggers:
+
+ - on save
+ - on watcher "changed/created/renamed/deleted"
+ - on periodic reconciliation (e.g., at app start or every N minutes per location)
+
+### Query features
+
+- Full-text search with snippet results
+- Filters:
+ - location
+ - file type
+ - updated range
+- Sorting:
+ - relevance
+ - recently modified
+ - filename
+
+## Permissions & Security Model (Tauri v2)
+
+- Use Tauri **capabilities** to enable only:
+
+ - dialog open/save
+ - fs read/write/rename/mkdir/watch (scoped)
+- Leverage fs plugin scoping (glob-based) and prevent parent traversal. ([Tauri][tauri-fs])
+- Persist scopes across restarts via **persisted-scope** (explicitly register after fs plugin). ([Tauri][tauri-persisted-scope])
+
+## Defaults
+
+- **Rust is authoritative** for Markdown → HTML, and the UI is a "viewer" of that output.
+- Default profile is **GFM-safe**: rich features, but no unsafe raw HTML. ([Crates.io][comrak])
+- Use Comrak **sourcepos** as the backbone for editor↔preview sync; it's explicitly supported in Comrak render options (with known limitations around lists/inlines). ([Docs.rs][comrak-render])
+
+[comrak]: https://crates.io/crates/comrak "comrak - crates.io: Rust Package Registry"
+[tauri-calling-rust]: https://v2.tauri.app/develop/calling-rust/ "Calling Rust from the Frontend"
+[tauri-capabilities]: https://v2.tauri.app/security/capabilities/ "Capabilities"
+[tauri-store]: https://v2.tauri.app/plugin/store/ "Store"
+[tauri-persisted-scope]: https://v2.tauri.app/plugin/persisted-scope/ "Persisted Scope"
+[tauri-fs]: https://v2.tauri.app/reference/javascript/fs/ "@tauri-apps/plugin-fs | Tauri"
+[comrak-render]: https://docs.rs/comrak/latest/comrak/options/struct.Render.html "Render in comrak::options - Rust"
diff --git a/docs/roadmap.md b/docs/roadmap.md
index a9d14f0..bc4c4d4 100644
--- a/docs/roadmap.md
+++ b/docs/roadmap.md
@@ -1,33 +1,5 @@
# Roadmap
-## Assumptions
-
-- **Canonical storage = OS locations (folders)** + **sidecar SQLite index + JSON settings**.
-- Markdown parsing/rendering is **authoritative in Rust** (single source of truth), using a spec-complete CommonMark+GFM engine (recommended: **Comrak**). ([Crates.io][1])
-- Frontend uses **Elm architecture** with `Model/Msg/update/Cmd/Sub`.
-- Rust communicates with the frontend via **Tauri commands + events**. ([Tauri][2])
-
-## Repo skeleton, contracts, and fixtures
-
-### Deliverables
-
-- Workspace layout:
- - `src/` (React UI)
- - `crates/core/` (`writer-core` domain types, markdown API, storage API)
- - `crates/markdown/` (`writer-md` markdown engine wrapper around Comrak)
- - `crates/store/` (`writer-store` SQLite + FTS)
- - `src-tauri/` (`writer` Tauri app + command handlers)
-- "Fixtures" folder:
- - `fixtures/markdown/` with input markdown + expected HTML + expected outline metadata
- - `fixtures/render/` for XSS / raw HTML testcases
-
-## Verification
-
-- CI runs:
- - Rust unit tests for markdown fixtures
- - UI typecheck + lint
-- A single "hello command" roundtrip: UI invokes `ping` and prints result. ([Tauri][2])
-
## Location system + permissions + persisted access
Users add folders ("locations"), and access persists across restarts—without granting broad filesystem access.
@@ -51,11 +23,6 @@ Users add folders ("locations"), and access persists across restarts—without g
- Validate roots still exist
- Emit events for missing/changed locations
-### Verification
-
-- Add location → restart app → still lists location and can read a test file within it.
-- Attempt open outside location root → must fail with a structured error.
-
## Rust command layer "ports" + event channel (foundation for Elm Cmd/Sub)
A stable backend API surface that the Elm-style frontend treats as "ports".
@@ -76,10 +43,6 @@ A stable backend API surface that the Elm-style frontend treats as "ports".
- `Sub` variants:
- backend events stream → `Msg::BackendEvent(...)`
-### Verification
-
-- Manual: trigger a backend event (e.g., "tick") and see it update UI state without direct UI polling.
-
## Document catalog + safe IO + atomic saves
Open/edit/save files in locations safely and predictably.
@@ -99,11 +62,6 @@ Open/edit/save files in locations safely and predictably.
- Debounced save in Elm (`Msg::EditorChanged` → schedule save `Cmd`)
- Save status machine: `Idle | Dirty | Saving | Saved | Error`
-### Verification
-
-- Kill app mid-save: file is either old or new, never truncated/partial.
-- Modify file externally; open in app; verify reload prompt or auto-reconcile.
-
## Markdown engine (Rust): parse, render, and metadata extraction
A "thorough" Markdown pipeline: deterministic HTML, structured metadata, and source mapping support.
@@ -139,14 +97,6 @@ A "thorough" Markdown pipeline: deterministic HTML, structured metadata, and sou
- Outline JSON match
- Sourcepos presence for block nodes
-### Verification
-
-- Run fixture suite; add at least:
-
- - tables, tasklists, autolinks
- - nested blockquotes, code fences
- - "raw HTML" cases (ensure safe behavior by default)
-
## Editor MVP (React): CodeMirror 6 + Markdown language + Elm integration
A robust text editor that cooperates with Elm state management (no "hidden state surprises").
@@ -170,11 +120,6 @@ A robust text editor that cooperates with Elm state management (no "hidden state
- `Msg::SaveFinished`
- `Msg::DocOpened`
-### Verification
-
-- Open a 200KB markdown file and type smoothly.
-- No infinite loops between editor updates and Elm state reconciliation.
-
## Preview renderer + scroll/selection sync (editor ↔ rendered HTML)
High-quality Markdown rendering with predictable safety and a stable sync model.
@@ -197,11 +142,6 @@ High-quality Markdown rendering with predictable safety and a stable sync model.
- preview scroll → nearest sourcepos line
- Implement coarse sync first (block-level), refine later. ([Docs.rs][6])
-### Verification
-
-- Cursor on heading → preview scrolls to that heading block.
-- Scrolling preview updates "current section" indicator in editor.
-
## Indexing + search (SQLite FTS), driven by watcher + reconciliation
Fast global search across locations with correct incremental updates.
@@ -221,11 +161,6 @@ Fast global search across locations with correct incremental updates.
- `search(query, filters, limit) -> SearchHit[]` with snippets
-### Verification
-
-- Delete `app.db` → app still opens docs → "Rebuild index" works.
-- Modify file externally → index updates and search finds new content.
-
## Markdown "thoroughness" upgrades (extensions, diagnostics, export)
Make Markdown handling feel professional and predictable for writers.
@@ -249,11 +184,6 @@ Make Markdown handling feel professional and predictable for writers.
- HTML export (direct from Rust renderer)
- PDF export (later; separate milestone due to complexity)
-### Verification
-
-- "Export HTML" matches preview output exactly.
-- Diagnostics appear deterministically for fixtures.
-
## Hardening
### Tasks
@@ -271,17 +201,6 @@ Make Markdown handling feel professional and predictable for writers.
- Corrupt settings/workspace → app resets safely
- Missing location root → UI prompts to relink/remove
-### Verification
-
-- Load 10k-note library (or synthetic corpus) and keep UI responsive.
-- Fuzz-ish tests for markdown parser inputs (crash-free).
-
-## Defaults (to keep this "correct")
-
-- **Rust is authoritative** for Markdown → HTML, and the UI is a "viewer" of that output.
-- Default profile is **GFM-safe**: rich features, but no unsafe raw HTML. ([Crates.io][1])
-- Use Comrak **sourcepos** as the backbone for editor↔preview sync; it’s explicitly supported in Comrak render options (with known limitations around lists/inlines). ([Docs.rs][6])
-
[1]: https://crates.io/crates/comrak "comrak - crates.io: Rust Package Registry"
[2]: https://v2.tauri.app/develop/calling-rust/ "Calling Rust from the Frontend"
[3]: https://codesandbox.io/s/react-codemirror-example-codemirror-6-markdown-auto-languages-iudnj "react-codemirror-example (codemirror 6) (Markdown auto ..."
diff --git a/docs/spec.md b/docs/spec.md
index 79f876f..58e5e8a 100644
--- a/docs/spec.md
+++ b/docs/spec.md
@@ -5,53 +5,10 @@
A focused writing environment that treats **user-chosen folders ("locations")** as the
canonical source of truth (plain-text/Markdown), with a **derived local index** for fast
search, and an **optional sync story** that comes "for free" via iCloud/Dropbox/OneDrive/etc
-because the files live in those providers’ folders.
+because the files live in those providers' folders.
Persistence with "library of folders," and sidecar state *for indexes and UI state only*.
-## Constraints
-
-1. **React + Tauri** desktop app.
-2. **Hand-made Elm architecture** for state management (single source of truth `Model`, message-driven `update`, explicit `Cmd` effects, `Subscriptions`).
-3. Storage must follow **three patterns**:
- - **Filesystem as canonical store**
- - **Sidecar metadata**
- - **Sync as transport**
-4. Must use **OS "Locations"**: user picks folders via native dialogs; app persists access, and treats those folders as roots for documents.
-5. Security posture: Tauri **capabilities/permissions** and filesystem scoping, not "unrestricted arbitrary fs" by default. ([Tauri][1])
-
-## Data model
-
-### 2.1 Canonical content (user-controlled)
-
-- All user documents are **plain-text files** stored *in the user’s chosen locations* (e.g., `~/Documents/Writing`, `~/Library/Mobile Documents/...` for iCloud Drive, Dropbox folder, etc.).
-- Supported file types (MVP):
-
- - `.md` (Markdown)
- - `.txt` (plain text)
- - optional: `.mdx` later (non-MVP)
-
-The app **never requires** content to live in an app-private container.
-
-### 2.2 Sidecar metadata (app-controlled)
-
-All app-owned derived data lives in the app’s application data directory (e.g., `AppData`/`Application Support` equivalent), not inside user content roots.
-
-- `app.db` (SQLite)
-
- - Search index via **FTS** (FTS5 recommended)
- - Document catalog (path, file hash/mtime, size, encoding)
- - Tag index (derived from frontmatter or inline syntax)
- - "Recent documents", pin list, writing sessions stats
-- `settings.json` (small, human-readable)
-
- - UI preferences, editor settings, enabled locations list (see §3)
-- `workspace.json`
-
- - Window layout, last open doc, sidebar state, etc.
-
-> Tauri provides an official **Store plugin** for persisting small state to a file (async), which is appropriate for `settings/workspace` class data. ([Tauri][2])
-
## Locations & Persistence
### 3.1 How "Locations" work
@@ -59,125 +16,23 @@ All app-owned derived data lives in the app’s application data directory (e.g.
- Users add a location by choosing a folder via native dialog (`Open Directory…`).
- The app treats each location as a **root**; all document operations must resolve under a root (no "../../" traversal, no implicit global disk access).
-Tauri’s **dialog plugin** can open file/directory selectors; selected paths are added to filesystem/asset scopes **for the running session**, but the scope change is **not persisted across restarts** unless you persist it. ([Tauri][3])
+Tauri's **dialog plugin** can open file/directory selectors; selected paths are added to filesystem/asset scopes **for the running session**, but the scope change is **not persisted across restarts** unless you persist it. ([Tauri][3])
### 3.2 Persisting access in Tauri
-- Use Tauri’s **persisted-scope plugin** so that filesystem/asset scopes added through dialogs (and related APIs) are restored on relaunch. ([Tauri][4])
+- Use Tauri's **persisted-scope plugin** so that filesystem/asset scopes added through dialogs (and related APIs) are restored on relaunch. ([Tauri][4])
- Use Tauri **capabilities/permissions** to grant only what you need (dialog open/save, fs read/write/watch within scope). ([Tauri][1])
-- Use the **fs plugin’s scope configuration** (glob-scoped access) as a second line of defense. ([Tauri][5])
+- Use the **fs plugin's scope configuration** (glob-scoped access) as a second line of defense. ([Tauri][5])
### 3.3 Platform notes
-- **macOS App Sandbox / MAS**: persistent access to user-selected folders typically involves **security-scoped bookmarks**; Apple’s sandboxing docs explicitly describe using bookmarks that grant access when resolved. ([Apple Developer][6])
+- **macOS App Sandbox / MAS**: persistent access to user-selected folders typically involves **security-scoped bookmarks**; Apple's sandboxing docs explicitly describe using bookmarks that grant access when resolved. ([Apple Developer][6])
- Practical spec stance: if you plan Mac App Store distribution, design the "Location persistence layer" so it *can* be backed by security-scoped bookmarks; for non-MAS builds, Tauri persisted scope may be sufficient depending on entitlements and packaging.
- **Linux (Flatpak/Snap)**: sandboxed deployments often rely on **XDG portals**; the **Document portal** exposes external files to sandboxed apps via a controlled mount (`/run/user/$UID/doc/…`). ([Flatpak][7])
-- **Windows**: sandboxed models (UWP-like) preserve file-picker access using concepts like a **future-access list**; even if you’re not UWP, this is a useful conceptual model for "remembering user-granted access." ([Microsoft Learn][8])
+- **Windows**: sandboxed models (UWP-like) preserve file-picker access using concepts like a **future-access list**; even if you're not UWP, this is a useful conceptual model for "remembering user-granted access." ([Microsoft Learn][8])
**Key design decision:** implement a *Location Access Abstraction* so the rest of the app only deals with `LocationId -> ResolvedPathHandle`, regardless of platform-specific persistence.
-## Architecture
-
-### Frontend: Elm loop in React
-
-Define:
-
-- `Model` (single immutable state tree)
-- `Msg` (all possible events - TS enum)
-- `update(model, msg) -> [model, Cmd[]]`
-- `Cmd` (effects): invokes Rust commands, timers, file watchers subscription changes
-- `subscriptions(model) -> Sub[]`:
- - file watcher events
- - debounced autosave ticks
- - OS focus/blur, window events
-
-**Rule:** UI components are pure views of `Model`. They may *dispatch* `Msg`, but they do not perform I/O.
-
-### Core: Rust "ports" layer
-
-All filesystem and indexing operations happen in Rust commands (Tauri backend), because:
-
-- Tauri’s JS fs APIs are scoped and safe, but a Rust core gives you stronger control over:
- - atomic writes
- - canonicalization
- - watcher integration
- - cross-platform path correctness
-- Tauri’s security model centers on **capabilities and permissioned commands**. ([Tauri][1])
-
-**Core commands:**
-
-- `location_add_via_dialog() -> LocationDescriptor`
-- `location_remove(LocationId)`
-- `doc_list(LocationId, filter/sort) -> DocMeta[]`
-- `doc_open(DocId) -> { text, meta }`
-- `doc_save(DocId, text, policy) -> SaveResult`
-- `doc_rename/move/delete`
-- `index_rebuild(LocationId)`
-- `search(query, options) -> SearchHit[]`
-- `watch_enable(LocationId)` / `watch_disable(LocationId)` (or auto from subscriptions)
-
-## Storage behaviors
-
-### Writes must be atomic
-
-- Save pipeline:
-
- 1. write to temp file in same directory (or safe temp under location root)
- 2. fsync as available
- 3. rename/replace original
-- Update index after successful rename/replace.
-- If provider conflicts occur (e.g., Dropbox "conflicted copy"), treat as a new file and surface a conflict UI.
-
-### File identity & change detection
-
-Store in SQLite:
-
-- `path`
-- `location_id`
-- `mtime`, `size`
-- `content_hash` (fast hash; compute on open/save and optionally on watcher events)
-- `doc_id` stable key = `(location_id, normalized_relative_path)`; if path changes, doc_id changes unless you implement inode/file-id based identity.
-
-### Encoding & line endings
-
-- Assume UTF-8 by default; detect BOM and handle losslessly.
-- Preserve line endings on save unless user opts into normalization.
-
-## Indexing & search
-
-### Index policy
-
-- SQLite FTS index is **derived**:
-
- - It can always be rebuilt.
- - It must never be required for opening/editing a file.
-- Index update triggers:
-
- - on save
- - on watcher "changed/created/renamed/deleted"
- - on periodic reconciliation (e.g., at app start or every N minutes per location)
-
-### Query features
-
-- Full-text search with snippet results
-- Filters:
- - location
- - file type
- - updated range
-- Sorting:
- - relevance
- - recently modified
- - filename
-
-## Permissions & security model (Tauri v2)
-
-- Use Tauri **capabilities** to enable only:
-
- - dialog open/save
- - fs read/write/rename/mkdir/watch (scoped)
-- Leverage fs plugin scoping (glob-based) and prevent parent traversal. ([Tauri][5])
-- Persist scopes across restarts via **persisted-scope** (explicitly register after fs plugin). ([Tauri][4])
-
## UX
### Library (Locations-first)
@@ -230,12 +85,11 @@ If you truly want "OS locations" in a way that survives sandboxed distribution,
- Flatpak/Snap often route access through **portals/document portal**. ([Flatpak][7])
[1]: https://v2.tauri.app/security/capabilities/ "Capabilities"
-[2]: https://v2.tauri.app/plugin/store/ "Store"
[3]: https://v2.tauri.app/reference/javascript/dialog/ "tauri-apps/plugin-dialog"
[4]: https://v2.tauri.app/plugin/persisted-scope/ "Persisted Scope"
[5]: https://v2.tauri.app/reference/javascript/fs/ "@tauri-apps/plugin-fs | Tauri"
[6]: https://developer.apple.com/documentation/security/accessing-files-from-the-macos-app-sandbox "Accessing files from the macOS App Sandbox"
[7]: https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Documents.html "XDG Desktop Portal documentation"
-[8]: https://learn.microsoft.com/en-us/uwp/api/windows.storage.accesscache.storageitemaccesslist?view=winrt-26100&utm_source=chatgpt.com "StorageItemAccessList Class - Windows"
+[8]: https://learn.microsoft.com/en-us/uwp/api/windows.storage.accesscache.storageitemaccesslist?view=winrt-26100 "StorageItemAccessList Class - Windows"
[9]: https://v2.tauri.app/plugin/dialog/ "Dialog"
[10]: https://docs.rs/tauri-plugin-persisted-scope "tauri_plugin_persisted_scope - Rust"
diff --git a/docs/validate.md b/docs/validate.md
new file mode 100644
index 0000000..5215dbd
--- /dev/null
+++ b/docs/validate.md
@@ -0,0 +1,53 @@
+# Validation
+
+## Repo skeleton, contracts, and fixtures
+
+- [ ] CI runs Rust unit tests for markdown fixtures
+- [ ] CI runs UI typecheck + lint
+- [ ] "Hello command" roundtrip: UI invokes `ping` and prints result
+
+## Location system + permissions + persisted access
+
+- [ ] Add location → restart app → still lists location and can read a test file within it
+- [ ] Attempt open outside location root → must fail with a structured error
+
+## Rust command layer "ports" + event channel
+
+- [ ] Trigger a backend event (e.g. "tick") and see it update UI state without direct UI polling
+
+## Document catalog + safe IO + atomic saves
+
+- [ ] Kill app mid-save: file is either old or new, never truncated/partial
+- [ ] Modify file externally; open in app; verify reload prompt or auto-reconcile
+
+## Markdown engine (Rust): parse, render, and metadata extraction
+
+- [ ] Run fixture suite covering:
+ - [ ] Tables, tasklists, autolinks
+ - [ ] Nested blockquotes, code fences
+ - [ ] "Raw HTML" cases (ensure safe behavior by default)
+
+## Editor MVP (React): CodeMirror 6 + Markdown language + Elm integration
+
+- [ ] Open a 200 KB markdown file and type smoothly
+- [ ] No infinite loops between editor updates and Elm state reconciliation
+
+## Preview renderer + scroll/selection sync
+
+- [ ] Cursor on heading → preview scrolls to that heading block
+- [ ] Scrolling preview updates "current section" indicator in editor
+
+## Indexing + search (SQLite FTS)
+
+- [ ] Delete `app.db` → app still opens docs → "Rebuild index" works
+- [ ] Modify file externally → index updates and search finds new content
+
+## Markdown "thoroughness" upgrades (extensions, diagnostics, export)
+
+- [ ] "Export HTML" matches preview output exactly
+- [ ] Diagnostics appear deterministically for fixtures
+
+## Hardening
+
+- [ ] Load 10k-note library (or synthetic corpus) and keep UI responsive
+- [ ] Fuzz-ish tests for markdown parser inputs (crash-free)
diff --git a/dprint.json b/dprint.json
new file mode 100644
index 0000000..f1ba1a3
--- /dev/null
+++ b/dprint.json
@@ -0,0 +1,6 @@
+{
+ "typescript": { "preferSingleLine": true, "jsx.bracketPosition": "sameLine", "jsxElement.preferSingleLine": true },
+ "json": { "preferSingleLine": true, "lineWidth": 120, "indentWidth": 2 },
+ "excludes": ["**/node_modules", "apps/web/public/pdf.worker.min.mjs"],
+ "plugins": ["https://plugins.dprint.dev/typescript-0.95.8.wasm", "https://plugins.dprint.dev/json-0.20.0.wasm"]
+}
diff --git a/package.json b/package.json
index e843acd..1db53e3 100644
--- a/package.json
+++ b/package.json
@@ -10,17 +10,20 @@
"tauri": "tauri"
},
"dependencies": {
+ "@tailwindcss/vite": "^4.1.18",
+ "@tauri-apps/api": "^2",
+ "@tauri-apps/plugin-opener": "^2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
- "@tauri-apps/api": "^2",
- "@tauri-apps/plugin-opener": "^2"
+ "tailwindcss": "^4.1.18"
},
"devDependencies": {
+ "@tauri-apps/cli": "^2",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0",
+ "dprint": "^0.51.1",
"typescript": "~5.8.3",
- "vite": "^7.0.4",
- "@tauri-apps/cli": "^2"
+ "vite": "^7.0.4"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..433d588
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,1696 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@tailwindcss/vite':
+ specifier: ^4.1.18
+ version: 4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))
+ '@tauri-apps/api':
+ specifier: ^2
+ version: 2.10.1
+ '@tauri-apps/plugin-opener':
+ specifier: ^2
+ version: 2.5.3
+ react:
+ specifier: ^19.1.0
+ version: 19.2.4
+ react-dom:
+ specifier: ^19.1.0
+ version: 19.2.4(react@19.2.4)
+ tailwindcss:
+ specifier: ^4.1.18
+ version: 4.1.18
+ devDependencies:
+ '@tauri-apps/cli':
+ specifier: ^2
+ version: 2.10.0
+ '@types/react':
+ specifier: ^19.1.8
+ version: 19.2.14
+ '@types/react-dom':
+ specifier: ^19.1.6
+ version: 19.2.3(@types/react@19.2.14)
+ '@vitejs/plugin-react':
+ specifier: ^4.6.0
+ version: 4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))
+ dprint:
+ specifier: ^0.51.1
+ version: 0.51.1
+ typescript:
+ specifier: ~5.8.3
+ version: 5.8.3
+ vite:
+ specifier: ^7.0.4
+ version: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2)
+
+packages:
+
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.0':
+ resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.0':
+ resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.1':
+ resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.6':
+ resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.0':
+ resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1':
+ resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1':
+ resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.0':
+ resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
+ engines: {node: '>=6.9.0'}
+
+ '@dprint/darwin-arm64@0.51.1':
+ resolution: {integrity: sha512-C7fkaz0/NGf/X4G9Cq65izdJgerND5jWShOaPiOgGs4A0CyCMKMLRd45m3xKIttuO8x0IQiZVixD22qmVglXmQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@dprint/darwin-x64@0.51.1':
+ resolution: {integrity: sha512-/CESb4SJejshTWb/nO2gucrioKdLD/fK2gKB5Immi4XecAjhkV1HZpUMRLsIeVDQ6ha7xktKbnnvudJRI0maSg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@dprint/linux-arm64-glibc@0.51.1':
+ resolution: {integrity: sha512-W+QD/4TkQKMPq02uz+ZIz/EI91YEtS2zHBu/Jihx2lfNh3HjUKgyY1KfqCTg2WGJVDkJtPc5KDw+M8yd4rI+Rg==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@dprint/linux-arm64-musl@0.51.1':
+ resolution: {integrity: sha512-P4Cfn8wrdSvdoMoz0HtsC5zZpbh8Kk3OjRzgG4qs/gub5Ba3CCLcVaQvQmIOCvgPzdhPB41eSn5qAK87mh7vCQ==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@dprint/linux-loong64-glibc@0.51.1':
+ resolution: {integrity: sha512-dxDShELBrSGB9PktRqhYrm1UWCL+7N430rGuDTFg+i4d1wL4JD7EI5NDS/5IVbg3lrKJUu6gAJoY9NWF4sZ0mQ==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@dprint/linux-loong64-musl@0.51.1':
+ resolution: {integrity: sha512-l+uNPzUBNxkywGN1HgvTV0CzUDpzXC+s6VEvjYRdVAEKl9TAsIfLMfZifS8rnlOi9hz/0sckmpny+wL40G6dhQ==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@dprint/linux-riscv64-glibc@0.51.1':
+ resolution: {integrity: sha512-YI6SemJRMsLUrtognhkAYOJ67j/AkiufHfx6+cPv/7qO6zBoBL6otR1e5yFYvAgEJOzRZD5g51EdqCcuK19B+g==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@dprint/linux-x64-glibc@0.51.1':
+ resolution: {integrity: sha512-Hm9ntOWeclXU6/rZGYIN2XjuAhsRUZ77lRJC5EiM1rP6Ayh0J/YMsDdqlPAs+xZGdFFzt8ZeUpqJJJsbc54ZjQ==}
+ cpu: [x64]
+ os: [linux]
+
+ '@dprint/linux-x64-musl@0.51.1':
+ resolution: {integrity: sha512-iUs/9Z4Ky6+2GAK+i1jF1ZwM27CSIGaqw3LxTXtmE8eBCRwyCW+opzn9bzaFtlT9REUAGMC5PXihAPmzP3ttWg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@dprint/win32-arm64@0.51.1':
+ resolution: {integrity: sha512-oXjEL7Blf/160/j7U8MapRzSOVQvLIdAKEZbslIHA4Fwj10fvoDizqpfVU/aVj/BVrlIcZ/+AH8kfSC9XAyjLw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@dprint/win32-x64@0.51.1':
+ resolution: {integrity: sha512-RM9thb/+Jm6GkXJS+eW+0Pzmz2PNeDk8ZOWcjunE1jv2RabHgm+GbeGpS+WZPOM9W3LSntswv6f4wjzfUkWD0g==}
+ cpu: [x64]
+ os: [win32]
+
+ '@esbuild/aix-ppc64@0.27.3':
+ resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.3':
+ resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.3':
+ resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.3':
+ resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.3':
+ resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.3':
+ resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.3':
+ resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.3':
+ resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.3':
+ resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.3':
+ resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.3':
+ resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.3':
+ resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.3':
+ resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.3':
+ resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.3':
+ resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.3':
+ resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.3':
+ resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.3':
+ resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.3':
+ resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.3':
+ resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.3':
+ resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.3':
+ resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.3':
+ resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.3':
+ resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.3':
+ resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@rolldown/pluginutils@1.0.0-beta.27':
+ resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+
+ '@rollup/rollup-android-arm-eabi@4.57.1':
+ resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==}
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.57.1':
+ resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==}
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.57.1':
+ resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.57.1':
+ resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.57.1':
+ resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.57.1':
+ resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.57.1':
+ resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.57.1':
+ resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-gnu@4.57.1':
+ resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-arm64-musl@4.57.1':
+ resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-gnu@4.57.1':
+ resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-loong64-musl@4.57.1':
+ resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
+ cpu: [loong64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.57.1':
+ resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-ppc64-musl@4.57.1':
+ resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.57.1':
+ resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-riscv64-musl@4.57.1':
+ resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@rollup/rollup-linux-s390x-gnu@4.57.1':
+ resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-gnu@4.57.1':
+ resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-linux-x64-musl@4.57.1':
+ resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@rollup/rollup-openbsd-x64@4.57.1':
+ resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@rollup/rollup-openharmony-arm64@4.57.1':
+ resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.57.1':
+ resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.57.1':
+ resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.57.1':
+ resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.57.1':
+ resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/node@4.1.18':
+ resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.18':
+ resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.18':
+ resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.18':
+ resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.18':
+ resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
+ resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
+ resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
+ resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
+ resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.18':
+ resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.18':
+ resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
+ resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.18':
+ resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.18':
+ resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/vite@4.1.18':
+ resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==}
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7
+
+ '@tauri-apps/api@2.10.1':
+ resolution: {integrity: sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==}
+
+ '@tauri-apps/cli-darwin-arm64@2.10.0':
+ resolution: {integrity: sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tauri-apps/cli-darwin-x64@2.10.0':
+ resolution: {integrity: sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tauri-apps/cli-linux-arm-gnueabihf@2.10.0':
+ resolution: {integrity: sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tauri-apps/cli-linux-arm64-gnu@2.10.0':
+ resolution: {integrity: sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tauri-apps/cli-linux-arm64-musl@2.10.0':
+ resolution: {integrity: sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tauri-apps/cli-linux-riscv64-gnu@2.10.0':
+ resolution: {integrity: sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==}
+ engines: {node: '>= 10'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@tauri-apps/cli-linux-x64-gnu@2.10.0':
+ resolution: {integrity: sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tauri-apps/cli-linux-x64-musl@2.10.0':
+ resolution: {integrity: sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tauri-apps/cli-win32-arm64-msvc@2.10.0':
+ resolution: {integrity: sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tauri-apps/cli-win32-ia32-msvc@2.10.0':
+ resolution: {integrity: sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==}
+ engines: {node: '>= 10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@tauri-apps/cli-win32-x64-msvc@2.10.0':
+ resolution: {integrity: sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tauri-apps/cli@2.10.0':
+ resolution: {integrity: sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==}
+ engines: {node: '>= 10'}
+ hasBin: true
+
+ '@tauri-apps/plugin-opener@2.5.3':
+ resolution: {integrity: sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.14':
+ resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
+
+ '@vitejs/plugin-react@4.7.0':
+ resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
+
+ baseline-browser-mapping@2.9.19:
+ resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==}
+ hasBin: true
+
+ browserslist@4.28.1:
+ resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ caniuse-lite@1.0.30001770:
+ resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ dprint@0.51.1:
+ resolution: {integrity: sha512-CEx+wYARxLAe9o7RCZ77GKae6DF7qjn5Rd98xbWdA3hB4PFBr+kHwLANmNHscNumBAIrCg5ZJj/Kz+OYbJ+GBA==}
+ hasBin: true
+
+ electron-to-chromium@1.5.286:
+ resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==}
+
+ enhanced-resolve@5.19.0:
+ resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==}
+ engines: {node: '>=10.13.0'}
+
+ esbuild@0.27.3:
+ resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ lightningcss-android-arm64@1.30.2:
+ resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.30.2:
+ resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.2:
+ resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.2:
+ resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.2:
+ resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.2:
+ resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.2:
+ resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.2:
+ resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.2:
+ resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.2:
+ resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.2:
+ resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.11:
+ resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ node-releases@2.0.27:
+ resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@4.0.3:
+ resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+ engines: {node: '>=12'}
+
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ react-dom@19.2.4:
+ resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
+ peerDependencies:
+ react: ^19.2.4
+
+ react-refresh@0.17.0:
+ resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
+ engines: {node: '>=0.10.0'}
+
+ react@19.2.4:
+ resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==}
+ engines: {node: '>=0.10.0'}
+
+ rollup@4.57.1:
+ resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ hasBin: true
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ tailwindcss@4.1.18:
+ resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==}
+
+ tapable@2.3.0:
+ resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+ engines: {node: '>=6'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ typescript@5.8.3:
+ resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ vite@7.3.1:
+ resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+snapshots:
+
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.0': {}
+
+ '@babel/core@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@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.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.1':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.28.6':
+ dependencies:
+ '@babel/compat-data': 7.29.0
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.1
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-module-imports@7.28.6':
+ dependencies:
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.28.6': {}
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.28.5': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.6':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+
+ '@babel/parser@7.29.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+
+ '@babel/traverse@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.29.0
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@dprint/darwin-arm64@0.51.1':
+ optional: true
+
+ '@dprint/darwin-x64@0.51.1':
+ optional: true
+
+ '@dprint/linux-arm64-glibc@0.51.1':
+ optional: true
+
+ '@dprint/linux-arm64-musl@0.51.1':
+ optional: true
+
+ '@dprint/linux-loong64-glibc@0.51.1':
+ optional: true
+
+ '@dprint/linux-loong64-musl@0.51.1':
+ optional: true
+
+ '@dprint/linux-riscv64-glibc@0.51.1':
+ optional: true
+
+ '@dprint/linux-x64-glibc@0.51.1':
+ optional: true
+
+ '@dprint/linux-x64-musl@0.51.1':
+ optional: true
+
+ '@dprint/win32-arm64@0.51.1':
+ optional: true
+
+ '@dprint/win32-x64@0.51.1':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.27.3':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/android-arm@0.27.3':
+ optional: true
+
+ '@esbuild/android-x64@0.27.3':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.3':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.3':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.3':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.3':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.3':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.3':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.3':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.3':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.3':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.3':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.3':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.3':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.3':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@rolldown/pluginutils@1.0.0-beta.27': {}
+
+ '@rollup/rollup-android-arm-eabi@4.57.1':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.57.1':
+ optional: true
+
+ '@rollup/rollup-openbsd-x64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.57.1':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.57.1':
+ optional: true
+
+ '@tailwindcss/node@4.1.18':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.19.0
+ jiti: 2.6.1
+ lightningcss: 1.30.2
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.18
+
+ '@tailwindcss/oxide-android-arm64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.18':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.18':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.18
+ '@tailwindcss/oxide-darwin-arm64': 4.1.18
+ '@tailwindcss/oxide-darwin-x64': 4.1.18
+ '@tailwindcss/oxide-freebsd-x64': 4.1.18
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.18
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.18
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.18
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.18
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.18
+
+ '@tailwindcss/vite@4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))':
+ dependencies:
+ '@tailwindcss/node': 4.1.18
+ '@tailwindcss/oxide': 4.1.18
+ tailwindcss: 4.1.18
+ vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2)
+
+ '@tauri-apps/api@2.10.1': {}
+
+ '@tauri-apps/cli-darwin-arm64@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-darwin-x64@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-linux-arm-gnueabihf@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-linux-arm64-gnu@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-linux-arm64-musl@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-linux-riscv64-gnu@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-linux-x64-gnu@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-linux-x64-musl@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-win32-arm64-msvc@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-win32-ia32-msvc@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli-win32-x64-msvc@2.10.0':
+ optional: true
+
+ '@tauri-apps/cli@2.10.0':
+ optionalDependencies:
+ '@tauri-apps/cli-darwin-arm64': 2.10.0
+ '@tauri-apps/cli-darwin-x64': 2.10.0
+ '@tauri-apps/cli-linux-arm-gnueabihf': 2.10.0
+ '@tauri-apps/cli-linux-arm64-gnu': 2.10.0
+ '@tauri-apps/cli-linux-arm64-musl': 2.10.0
+ '@tauri-apps/cli-linux-riscv64-gnu': 2.10.0
+ '@tauri-apps/cli-linux-x64-gnu': 2.10.0
+ '@tauri-apps/cli-linux-x64-musl': 2.10.0
+ '@tauri-apps/cli-win32-arm64-msvc': 2.10.0
+ '@tauri-apps/cli-win32-ia32-msvc': 2.10.0
+ '@tauri-apps/cli-win32-x64-msvc': 2.10.0
+
+ '@tauri-apps/plugin-opener@2.5.3':
+ dependencies:
+ '@tauri-apps/api': 2.10.1
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.29.0
+ '@babel/types': 7.29.0
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/estree@1.0.8': {}
+
+ '@types/react-dom@19.2.3(@types/react@19.2.14)':
+ dependencies:
+ '@types/react': 19.2.14
+
+ '@types/react@19.2.14':
+ dependencies:
+ csstype: 3.2.3
+
+ '@vitejs/plugin-react@4.7.0(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
+ '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0)
+ '@rolldown/pluginutils': 1.0.0-beta.27
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.17.0
+ vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2)
+ transitivePeerDependencies:
+ - supports-color
+
+ baseline-browser-mapping@2.9.19: {}
+
+ browserslist@4.28.1:
+ dependencies:
+ baseline-browser-mapping: 2.9.19
+ caniuse-lite: 1.0.30001770
+ electron-to-chromium: 1.5.286
+ node-releases: 2.0.27
+ update-browserslist-db: 1.2.3(browserslist@4.28.1)
+
+ caniuse-lite@1.0.30001770: {}
+
+ convert-source-map@2.0.0: {}
+
+ csstype@3.2.3: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ detect-libc@2.1.2: {}
+
+ dprint@0.51.1:
+ optionalDependencies:
+ '@dprint/darwin-arm64': 0.51.1
+ '@dprint/darwin-x64': 0.51.1
+ '@dprint/linux-arm64-glibc': 0.51.1
+ '@dprint/linux-arm64-musl': 0.51.1
+ '@dprint/linux-loong64-glibc': 0.51.1
+ '@dprint/linux-loong64-musl': 0.51.1
+ '@dprint/linux-riscv64-glibc': 0.51.1
+ '@dprint/linux-x64-glibc': 0.51.1
+ '@dprint/linux-x64-musl': 0.51.1
+ '@dprint/win32-arm64': 0.51.1
+ '@dprint/win32-x64': 0.51.1
+
+ electron-to-chromium@1.5.286: {}
+
+ enhanced-resolve@5.19.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
+ esbuild@0.27.3:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.3
+ '@esbuild/android-arm': 0.27.3
+ '@esbuild/android-arm64': 0.27.3
+ '@esbuild/android-x64': 0.27.3
+ '@esbuild/darwin-arm64': 0.27.3
+ '@esbuild/darwin-x64': 0.27.3
+ '@esbuild/freebsd-arm64': 0.27.3
+ '@esbuild/freebsd-x64': 0.27.3
+ '@esbuild/linux-arm': 0.27.3
+ '@esbuild/linux-arm64': 0.27.3
+ '@esbuild/linux-ia32': 0.27.3
+ '@esbuild/linux-loong64': 0.27.3
+ '@esbuild/linux-mips64el': 0.27.3
+ '@esbuild/linux-ppc64': 0.27.3
+ '@esbuild/linux-riscv64': 0.27.3
+ '@esbuild/linux-s390x': 0.27.3
+ '@esbuild/linux-x64': 0.27.3
+ '@esbuild/netbsd-arm64': 0.27.3
+ '@esbuild/netbsd-x64': 0.27.3
+ '@esbuild/openbsd-arm64': 0.27.3
+ '@esbuild/openbsd-x64': 0.27.3
+ '@esbuild/openharmony-arm64': 0.27.3
+ '@esbuild/sunos-x64': 0.27.3
+ '@esbuild/win32-arm64': 0.27.3
+ '@esbuild/win32-ia32': 0.27.3
+ '@esbuild/win32-x64': 0.27.3
+
+ escalade@3.2.0: {}
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ fsevents@2.3.3:
+ optional: true
+
+ gensync@1.0.0-beta.2: {}
+
+ graceful-fs@4.2.11: {}
+
+ jiti@2.6.1: {}
+
+ js-tokens@4.0.0: {}
+
+ jsesc@3.1.0: {}
+
+ json5@2.2.3: {}
+
+ lightningcss-android-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-arm64@1.30.2:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.2:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.2:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.2:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.2:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.2:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.2:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.2:
+ optional: true
+
+ lightningcss@1.30.2:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.30.2
+ lightningcss-darwin-arm64: 1.30.2
+ lightningcss-darwin-x64: 1.30.2
+ lightningcss-freebsd-x64: 1.30.2
+ lightningcss-linux-arm-gnueabihf: 1.30.2
+ lightningcss-linux-arm64-gnu: 1.30.2
+ lightningcss-linux-arm64-musl: 1.30.2
+ lightningcss-linux-x64-gnu: 1.30.2
+ lightningcss-linux-x64-musl: 1.30.2
+ lightningcss-win32-arm64-msvc: 1.30.2
+ lightningcss-win32-x64-msvc: 1.30.2
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.11: {}
+
+ node-releases@2.0.27: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@4.0.3: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ react-dom@19.2.4(react@19.2.4):
+ dependencies:
+ react: 19.2.4
+ scheduler: 0.27.0
+
+ react-refresh@0.17.0: {}
+
+ react@19.2.4: {}
+
+ rollup@4.57.1:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.57.1
+ '@rollup/rollup-android-arm64': 4.57.1
+ '@rollup/rollup-darwin-arm64': 4.57.1
+ '@rollup/rollup-darwin-x64': 4.57.1
+ '@rollup/rollup-freebsd-arm64': 4.57.1
+ '@rollup/rollup-freebsd-x64': 4.57.1
+ '@rollup/rollup-linux-arm-gnueabihf': 4.57.1
+ '@rollup/rollup-linux-arm-musleabihf': 4.57.1
+ '@rollup/rollup-linux-arm64-gnu': 4.57.1
+ '@rollup/rollup-linux-arm64-musl': 4.57.1
+ '@rollup/rollup-linux-loong64-gnu': 4.57.1
+ '@rollup/rollup-linux-loong64-musl': 4.57.1
+ '@rollup/rollup-linux-ppc64-gnu': 4.57.1
+ '@rollup/rollup-linux-ppc64-musl': 4.57.1
+ '@rollup/rollup-linux-riscv64-gnu': 4.57.1
+ '@rollup/rollup-linux-riscv64-musl': 4.57.1
+ '@rollup/rollup-linux-s390x-gnu': 4.57.1
+ '@rollup/rollup-linux-x64-gnu': 4.57.1
+ '@rollup/rollup-linux-x64-musl': 4.57.1
+ '@rollup/rollup-openbsd-x64': 4.57.1
+ '@rollup/rollup-openharmony-arm64': 4.57.1
+ '@rollup/rollup-win32-arm64-msvc': 4.57.1
+ '@rollup/rollup-win32-ia32-msvc': 4.57.1
+ '@rollup/rollup-win32-x64-gnu': 4.57.1
+ '@rollup/rollup-win32-x64-msvc': 4.57.1
+ fsevents: 2.3.3
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
+ source-map-js@1.2.1: {}
+
+ tailwindcss@4.1.18: {}
+
+ tapable@2.3.0: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ typescript@5.8.3: {}
+
+ update-browserslist-db@1.2.3(browserslist@4.28.1):
+ dependencies:
+ browserslist: 4.28.1
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2):
+ dependencies:
+ esbuild: 0.27.3
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.57.1
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ fsevents: 2.3.3
+ jiti: 2.6.1
+ lightningcss: 1.30.2
+
+ yallist@3.1.1: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..5603573
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,3 @@
+onlyBuiltDependencies:
+ - dprint
+ - esbuild
diff --git a/src/App.css b/src/App.css
index 85f7a4a..f1d8c73 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,116 +1 @@
-.logo.vite:hover {
- filter: drop-shadow(0 0 2em #747bff);
-}
-
-.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafb);
-}
-:root {
- font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
- font-size: 16px;
- line-height: 24px;
- font-weight: 400;
-
- color: #0f0f0f;
- background-color: #f6f6f6;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- -webkit-text-size-adjust: 100%;
-}
-
-.container {
- margin: 0;
- padding-top: 10vh;
- display: flex;
- flex-direction: column;
- justify-content: center;
- text-align: center;
-}
-
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: 0.75s;
-}
-
-.logo.tauri:hover {
- filter: drop-shadow(0 0 2em #24c8db);
-}
-
-.row {
- display: flex;
- justify-content: center;
-}
-
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-
-a:hover {
- color: #535bf2;
-}
-
-h1 {
- text-align: center;
-}
-
-input,
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- color: #0f0f0f;
- background-color: #ffffff;
- transition: border-color 0.25s;
- box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
-}
-
-button {
- cursor: pointer;
-}
-
-button:hover {
- border-color: #396cd8;
-}
-button:active {
- border-color: #396cd8;
- background-color: #e8e8e8;
-}
-
-input,
-button {
- outline: none;
-}
-
-#greet-input {
- margin-right: 5px;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- color: #f6f6f6;
- background-color: #2f2f2f;
- }
-
- a:hover {
- color: #24c8db;
- }
-
- input,
- button {
- color: #ffffff;
- background-color: #0f0f0f98;
- }
- button:active {
- background-color: #0f0f0f69;
- }
-}
+@import "tailwindcss";
diff --git a/src/App.tsx b/src/App.tsx
index 8286a76..674aae1 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,6 +1,5 @@
-import { useState } from "react";
-import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/core";
+import { useState } from "react";
import "./App.css";
function App() {
@@ -23,9 +22,7 @@ function App() {
-
-
-
+ Logo
Click on the Tauri, Vite, and React logos to learn more.
@@ -34,13 +31,8 @@ function App() { onSubmit={(e) => { e.preventDefault(); greet(); - }} - > - setName(e.currentTarget.value)} - placeholder="Enter a name..." - /> + }}> + setName(e.currentTarget.value)} placeholder="Enter a name..." />{greetMsg}
diff --git a/src/assets/react.svg b/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index ddad22a..8ff1b65 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,32 +1,26 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; // @ts-expect-error process is a nodejs global const host = process.env.TAURI_DEV_HOST; -// https://vite.dev/config/ export default defineConfig(async () => ({ - plugins: [react()], - - // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // - // 1. prevent Vite from obscuring rust errors - clearScreen: false, - // 2. tauri expects a fixed port, fail if that port is not available - server: { - port: 1420, - strictPort: true, - host: host || false, - hmr: host - ? { - protocol: "ws", - host, - port: 1421, - } - : undefined, - watch: { - // 3. tell Vite to ignore watching `src-tauri` - ignored: ["**/src-tauri/**"], + plugins: [react(), tailwindcss()], + clearScreen: false, + server: { + port: 1420, + strictPort: true, + host: host || false, + hmr: host + ? { + protocol: "ws", + host, + port: 1421, + } + : undefined, + watch: { + ignored: ["**/src-tauri/**"], + }, }, - }, }));