Explorar el Código

feat(tauri): add D1 data layer — SQLite index, file watcher, Pinia store, tests

- Add Rust commands: read_file, write_file, list_directory, create_directory
- Add file watcher via notify crate (Tauri event emission, .md only, in-flight tracking)
- Add tauri-plugin-dialog, tauri-plugin-store, tauri-plugin-sql (SQLite features)
- Configure permissions: fs read/write, dialog, store, sql
- Add Pinia workspace store with index initialization and watcher integration
- Add indexer: fullReindex (walk workspace), indexFile (block diff + [[link]] extraction),
  removeFile (cascade delete), FTS5 with sync triggers
- Add schema: pages, blocks (with content_hash, block_type enum), links (with offset),
  blocks_fts (FTS5), all indices
- Add settings composable (tauri-plugin-store) and file system composable (invoke wrapper)
- Add vitest + 31 unit tests for parser functions and schema integrity
- Export splitMarkdownIntoBlocks from editor public API
Zander Hawke hace 19 horas
padre
commit
4d7c6730fe

+ 1 - 1
apps/tauri/nuxt.config.ts

@@ -5,7 +5,7 @@ const EDITOR_ROOT = resolve(__dirname, "../../packages/editor")
 export default defineNuxtConfig({
   ssr: false,
 
-  modules: ["@nuxt/ui"],
+  modules: ["@nuxt/ui", "@pinia/nuxt"],
 
   css: [
     "~/assets/main.css",

+ 9 - 1
apps/tauri/package.json

@@ -8,23 +8,31 @@
     "build": "nuxt build",
     "generate": "nuxt generate",
     "preview": "nuxt preview",
-    "tauri": "tauri"
+    "tauri": "tauri",
+    "test": "vitest run"
   },
   "dependencies": {
     "@enesis/editor": "workspace:*",
     "@fontsource-variable/geist": "^5.2.9",
     "@nuxt/ui": "^4.7.1",
     "@nuxtjs/color-mode": "^4.0.1",
+    "@pinia/nuxt": "^0.11.3",
     "@tauri-apps/api": "^2",
+    "@tauri-apps/plugin-dialog": "^2.7.1",
     "@tauri-apps/plugin-fs": "~2",
     "@tauri-apps/plugin-opener": "^2",
+    "@tauri-apps/plugin-sql": "^2.4.0",
+    "@tauri-apps/plugin-store": "^2.4.3",
     "nuxt": "^4.4.8",
     "vue": "^3.5.34",
     "vue-router": "^5.0.7"
   },
   "devDependencies": {
     "@tauri-apps/cli": "^2",
+    "@types/better-sqlite3": "^7.6.13",
+    "better-sqlite3": "^12.11.1",
     "typescript": "~6.0.2",
+    "vitest": "^4.1.7",
     "vue-tsc": "^3.2.8"
   }
 }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 657 - 27
apps/tauri/src-tauri/Cargo.lock


+ 4 - 0
apps/tauri/src-tauri/Cargo.toml

@@ -20,7 +20,11 @@ tauri-build = { version = "2", features = [] }
 [dependencies]
 tauri = { version = "2", features = [] }
 tauri-plugin-opener = "2"
+tauri-plugin-dialog = "2"
+tauri-plugin-store = "2"
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
 tauri-plugin-fs = "2"
+tauri-plugin-sql = { version = "2", features = ["sqlite"] }
+notify = "8.2.0"
 

+ 10 - 4
apps/tauri/src-tauri/capabilities/default.json

@@ -2,9 +2,7 @@
   "$schema": "../gen/schemas/desktop-schema.json",
   "identifier": "default",
   "description": "Capability for the main window",
-  "windows": [
-    "main"
-  ],
+  "windows": ["main"],
   "permissions": [
     "core:default",
     "opener:default",
@@ -13,6 +11,14 @@
     {
       "identifier": "fs:allow-read-text-file",
       "allow": [{ "path": "**" }]
-    }
+    },
+    {
+      "identifier": "fs:allow-write-text-file",
+      "allow": [{ "path": "**" }]
+    },
+    "dialog:default",
+    "store:default",
+    "sql:default",
+    "sql:allow-execute"
   ]
 }

+ 104 - 4
apps/tauri/src-tauri/src/lib.rs

@@ -1,7 +1,98 @@
-// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
+use std::fs;
+use std::path::Path;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use std::thread;
+use notify::{Config, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
+use serde::Serialize;
+use tauri::Emitter;
+
+/// Read the full contents of a file as a string.
+#[tauri::command]
+fn read_file(path: String) -> Result<String, String> {
+    fs::read_to_string(&path).map_err(|e| e.to_string())
+}
+
+/// Write a string to a file, creating or overwriting it.
+#[tauri::command]
+fn write_file(path: String, content: String) -> Result<(), String> {
+    fs::write(&path, &content).map_err(|e| e.to_string())
+}
+
+/// List all entries in a directory, sorted alphabetically.
+#[tauri::command]
+fn list_directory(path: String) -> Result<Vec<String>, String> {
+    let mut entries: Vec<String> = fs::read_dir(&path)
+        .map_err(|e| e.to_string())?
+        .filter_map(|entry| entry.ok())
+        .map(|entry| entry.path().to_string_lossy().to_string())
+        .collect();
+    entries.sort();
+    Ok(entries)
+}
+
+/// Create a directory and all parent directories if they don't exist.
 #[tauri::command]
-fn greet(name: &str) -> String {
-    format!("Hello, {}! You've been greeted from Rust!", name)
+fn create_directory(path: String) -> Result<(), String> {
+    fs::create_dir_all(&path).map_err(|e| e.to_string())
+}
+
+/// Event payload sent to the frontend when a file changes on disk.
+#[derive(Clone, Serialize)]
+struct FsEvent {
+    path: String,
+    kind: String,
+}
+
+/// Start a recursive file watcher on the workspace directory.
+///
+/// Emits `fs-watch:change` events to the frontend for `.md` file
+/// create/modify/delete events. The frontend should debounce and
+/// re-index only the changed file. The watcher runs until the app exits.
+#[tauri::command]
+fn start_watcher(app: tauri::AppHandle, workspace_path: String) -> Result<(), String> {
+    let running = Arc::new(AtomicBool::new(true));
+    let running_clone = running.clone();
+
+    let mut watcher = RecommendedWatcher::new(
+        move |res: Result<Event, notify::Error>| {
+            if let Ok(event) = res {
+                let kind = match event.kind {
+                    EventKind::Create(_) => "created",
+                    EventKind::Modify(_) => "modified",
+                    EventKind::Remove(_) => "deleted",
+                    _ => return,
+                };
+
+                for path in &event.paths {
+                    let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
+                    if ext != "md" {
+                        continue;
+                    }
+                    let payload = FsEvent {
+                        path: path.to_string_lossy().to_string(),
+                        kind: kind.to_string(),
+                    };
+                    let _ = app.emit("fs-watch:change", payload);
+                }
+            }
+        },
+        Config::default(),
+    )
+    .map_err(|e| e.to_string())?;
+
+    watcher
+        .watch(Path::new(&workspace_path), RecursiveMode::Recursive)
+        .map_err(|e| e.to_string())?;
+
+    // Keep the watcher alive by parking the thread
+    thread::spawn(move || {
+        while running_clone.load(Ordering::Relaxed) {
+            thread::sleep(std::time::Duration::from_secs(1));
+        }
+    });
+
+    Ok(())
 }
 
 #[cfg_attr(mobile, tauri::mobile_entry_point)]
@@ -9,7 +100,16 @@ pub fn run() {
     tauri::Builder::default()
         .plugin(tauri_plugin_fs::init())
         .plugin(tauri_plugin_opener::init())
-        .invoke_handler(tauri::generate_handler![greet])
+        .plugin(tauri_plugin_dialog::init())
+        .plugin(tauri_plugin_store::Builder::default().build())
+        .plugin(tauri_plugin_sql::Builder::default().build())
+        .invoke_handler(tauri::generate_handler![
+            read_file,
+            write_file,
+            list_directory,
+            create_directory,
+            start_watcher,
+        ])
         .run(tauri::generate_context!())
         .expect("error while running tauri application");
 }

+ 4 - 0
apps/tauri/src/app.vue

@@ -1,4 +1,8 @@
 <script setup lang="ts">
+import { useWorkspaceStore } from "~/stores/workspace"
+
+const workspace = useWorkspaceStore()
+workspace.initialize()
 </script>
 
 <template>

+ 33 - 0
apps/tauri/src/composables/useFileSystem.ts

@@ -0,0 +1,33 @@
+/**
+ * File system operations via Tauri invoke.
+ * Falls back to in-memory stubs when running in browser (dev mode).
+ */
+import { invoke } from "@tauri-apps/api/core"
+
+function isTauri(): boolean {
+  return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window
+}
+
+export function useFileSystem() {
+  async function readFile(path: string): Promise<string> {
+    if (!isTauri()) return ""
+    return await invoke<string>("read_file", { path })
+  }
+
+  async function writeFile(path: string, content: string): Promise<void> {
+    if (!isTauri()) return
+    await invoke("write_file", { path, content })
+  }
+
+  async function listDirectory(path: string): Promise<string[]> {
+    if (!isTauri()) return []
+    return await invoke<string[]>("list_directory", { path })
+  }
+
+  async function createDirectory(path: string): Promise<void> {
+    if (!isTauri()) return
+    await invoke("create_directory", { path })
+  }
+
+  return { readFile, writeFile, listDirectory, createDirectory }
+}

+ 42 - 0
apps/tauri/src/composables/useSettings.ts

@@ -0,0 +1,42 @@
+/**
+ * Persistent settings via Tauri store plugin.
+ * Stores key-value data in a JSON file managed by the Tauri backend.
+ * Falls back to in-memory Map when running in browser (dev mode).
+ */
+import { Store } from "@tauri-apps/plugin-store"
+
+let _store: Store | null = null
+const memoryStore = new Map<string, unknown>()
+
+function isTauri(): boolean {
+  return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window
+}
+
+async function getStore(): Promise<Store> {
+  if (!_store) {
+    _store = isTauri() ? await Store.load("settings.json") : null
+  }
+  return _store as Store
+}
+
+export function useSettings() {
+  async function get<T>(key: string): Promise<T | undefined> {
+    if (isTauri()) {
+      const store = await getStore()
+      return await store.get<T>(key)
+    }
+    return memoryStore.get(key) as T | undefined
+  }
+
+  async function set<T>(key: string, value: T): Promise<void> {
+    if (isTauri()) {
+      const store = await getStore()
+      await store.set(key, value)
+      await store.save()
+      return
+    }
+    memoryStore.set(key, value)
+  }
+
+  return { get, set }
+}

+ 159 - 0
apps/tauri/src/lib/indexer.ts

@@ -0,0 +1,159 @@
+/**
+ * Workspace indexer.
+ * Walks markdown files in the workspace, parses them into blocks using
+ * splitMarkdownIntoBlocks, and populates the SQLite index (pages, blocks, links, FTS5).
+ *
+ * The indexer is read-only — it never writes to markdown files. Blocks without
+ * id:: stamps get transient index-time IDs that are regenerated on re-index.
+ * Stamping (writing id:: to the file) happens in D2 when references are created.
+ *
+ * Content hash diffing skips blocks whose content hasn't changed, keeping
+ * incremental re-index fast and preserving rowids for in-memory references.
+ */
+
+import Database from "@tauri-apps/plugin-sql"
+import { splitMarkdownIntoBlocks } from "@enesis/editor"
+import { SCHEMA_SQL } from "./schema"
+import { contentHash, detectBlockType, extractTitle, extractLinks, type ExtractedLink } from "./parse"
+
+export interface IndexerOptions {
+  /** Tauri invoke wrapper for listing directory contents. */
+  listDirectory: (path: string) => Promise<string[]>
+  /** Tauri invoke wrapper for reading file contents. */
+  readFile: (path: string) => Promise<string>
+  /** Absolute path to the workspace root directory. */
+  workspacePath: string
+}
+
+/**
+ * Initialize the SQLite index at the given path.
+ * Creates the database file if it doesn't exist and runs CREATE TABLE IF NOT EXISTS.
+ * Tables, indices, FTS5 virtual table, and sync triggers are all idempotent.
+ */
+export async function initIndex(
+  dbPath: string,
+): Promise<Database> {
+  const db = await Database.load(`sqlite:${dbPath}`)
+  await db.execute(SCHEMA_SQL)
+  return db
+}
+
+/**
+ * Index a single markdown file into the database.
+ *
+ * - Upserts the page row (title, word count, modified_at)
+ * - Diffs existing block hashes against new content; skips unchanged blocks
+ * - Upserts changed or new blocks
+ * - Re-indexes links (delete-all + re-insert within each changed block)
+ * - Removes blocks that no longer exist in the file
+ */
+export async function indexFile(
+  db: Database,
+  filePath: string,
+  content: string,
+): Promise<void> {
+  const now = Math.floor(Date.now() / 1000)
+  const hash = contentHash(content)
+  const title = extractTitle(content)
+
+  await db.execute(
+    `INSERT INTO pages (path, title, modified_at, word_count)
+     VALUES ($1, $2, $3, $4)
+     ON CONFLICT(path) DO UPDATE SET
+       title = excluded.title,
+       modified_at = excluded.modified_at,
+       word_count = excluded.word_count`,
+    [filePath, title, now, content.split(/\s+/).length],
+  )
+
+  const existing = await db.select<{ id: string; content_hash: string }[]>(
+    "SELECT id, content_hash FROM blocks WHERE page_path = $1",
+    [filePath],
+  )
+  const existingMap = new Map(existing.map((b) => [b.id, b.content_hash]))
+
+  const blocks = splitMarkdownIntoBlocks(content)
+  const seenIds = new Set<string>()
+
+  for (const block of blocks) {
+    seenIds.add(block.id)
+    const blockHash = contentHash(block.content)
+
+    if (existingMap.get(block.id) === blockHash) continue
+
+    const blockType = detectBlockType(block.content)
+
+    await db.execute(
+      `INSERT INTO blocks (id, page_path, content, depth, block_type, has_id_stamp, content_hash, modified_at)
+       VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
+       ON CONFLICT(id) DO UPDATE SET
+         content = excluded.content,
+         depth = excluded.depth,
+         block_type = excluded.block_type,
+         content_hash = excluded.content_hash,
+         modified_at = excluded.modified_at`,
+      [block.id, filePath, block.content, block.depth, blockType, 0, blockHash, now],
+    )
+
+    // Re-index links for this block
+    await db.execute("DELETE FROM links WHERE source_block_id = $1", [block.id])
+    const links = extractLinks(block.content)
+    for (const link of links) {
+      await db.execute(
+        `INSERT INTO links (source_block_id, source_page_path, target, link_type, offset_in_block)
+         VALUES ($1, $2, $3, $4, $5)`,
+        [block.id, filePath, link.target, link.linkType, link.offset],
+      )
+    }
+  }
+
+  // Remove stale blocks
+  for (const existingId of existingMap.keys()) {
+    if (!seenIds.has(existingId)) {
+      await db.execute("DELETE FROM blocks WHERE id = $1", [existingId])
+    }
+  }
+}
+
+/**
+ * Remove a file and all its blocks/links from the index.
+ * Cascading deletes via the FK constraints handle blocks and links.
+ */
+export async function removeFile(
+  db: Database,
+  filePath: string,
+): Promise<void> {
+  await db.execute("DELETE FROM pages WHERE path = $1", [filePath])
+}
+
+/**
+ * Rebuild the entire index by walking every .md file in the workspace.
+ * Compares existing indexed pages against the filesystem and:
+ * - Indexes new or changed files
+ * - Removes pages that no longer exist
+ * - Skips files that fail to read (logs warning, continues)
+ */
+export async function fullReindex(
+  opts: IndexerOptions,
+  db: Database,
+): Promise<void> {
+  const allFiles = await opts.listDirectory(opts.workspacePath)
+  const mdFiles = allFiles.filter((f) => f.endsWith(".md"))
+
+  const indexed = await db.select<{ path: string }[]>("SELECT path FROM pages")
+  const indexedSet = new Set(indexed.map((p) => p.path))
+
+  for (const filePath of mdFiles) {
+    try {
+      const content = await opts.readFile(filePath)
+      await indexFile(db, filePath, content)
+    } catch (e) {
+      console.warn(`[indexer] failed to index ${filePath}:`, e)
+    }
+    indexedSet.delete(filePath)
+  }
+
+  for (const stalePath of indexedSet) {
+    await removeFile(db, stalePath)
+  }
+}

+ 129 - 0
apps/tauri/src/lib/parse.test.ts

@@ -0,0 +1,129 @@
+import { describe, it, expect } from "vitest"
+import { contentHash, detectBlockType, extractTitle, extractLinks } from "./parse"
+
+describe("contentHash", () => {
+  it("returns a deterministic hash for the same content", () => {
+    expect(contentHash("hello")).toBe(contentHash("hello"))
+  })
+
+  it("returns different hashes for different content", () => {
+    expect(contentHash("hello")).not.toBe(contentHash("world"))
+  })
+
+  it("handles empty string", () => {
+    expect(contentHash("")).toBe("0")
+  })
+})
+
+describe("detectBlockType", () => {
+  it("classifies heading", () => {
+    expect(detectBlockType("# Title")).toBe("heading")
+    expect(detectBlockType("## Subtitle")).toBe("heading")
+    expect(detectBlockType("###### H6")).toBe("heading")
+  })
+
+  it("classifies task states", () => {
+    expect(detectBlockType("TODO buy milk")).toBe("task")
+    expect(detectBlockType("DOING implement feature")).toBe("task")
+    expect(detectBlockType("DONE ship it")).toBe("task")
+    expect(detectBlockType("LATER maybe")).toBe("task")
+    expect(detectBlockType("NOW urgent")).toBe("task")
+    expect(detectBlockType("WAITING on review")).toBe("task")
+    expect(detectBlockType("CANCELLED wontfix")).toBe("task")
+  })
+
+  it("classifies callout", () => {
+    expect(detectBlockType("> [!NOTE] info")).toBe("callout")
+    expect(detectBlockType("> [!WARNING] caution")).toBe("callout")
+  })
+
+  it("classifies blockquote", () => {
+    expect(detectBlockType("> quoted text")).toBe("blockquote")
+  })
+
+  it("classifies code fence", () => {
+    expect(detectBlockType("```typescript")).toBe("code")
+    expect(detectBlockType("```")).toBe("code")
+  })
+
+  it("classifies table", () => {
+    expect(detectBlockType("| A | B |")).toBe("table")
+  })
+
+  it("classifies math block", () => {
+    expect(detectBlockType("$$")).toBe("math")
+    expect(detectBlockType("$$ E = mc^2 $$")).toBe("math")
+  })
+
+  it("classifies property", () => {
+    expect(detectBlockType("author:: John")).toBe("property")
+    expect(detectBlockType("date:: 2026-06-21")).toBe("property")
+  })
+
+  it("classifies plain text as paragraph", () => {
+    expect(detectBlockType("some text")).toBe("paragraph")
+    expect(detectBlockType("")).toBe("paragraph")
+  })
+})
+
+describe("extractTitle", () => {
+  it("extracts the first H1", () => {
+    expect(extractTitle("# Hello\n\nSome text")).toBe("Hello")
+  })
+
+  it("returns empty string when no H1", () => {
+    expect(extractTitle("## Not H1\n\nText")).toBe("")
+  })
+
+  it("returns empty string for empty content", () => {
+    expect(extractTitle("")).toBe("")
+  })
+})
+
+describe("extractLinks", () => {
+  it("extracts [[Page Name]] references", () => {
+    const links = extractLinks("see [[Other Page]] for details")
+    expect(links).toHaveLength(1)
+    expect(links[0]).toMatchObject({ target: "Other Page", linkType: "page-ref" })
+  })
+
+  it("extracts [[Page|Alias]] references", () => {
+    const links = extractLinks("see [[Page|Alias]] here")
+    expect(links).toHaveLength(1)
+    expect(links[0]).toMatchObject({ target: "Page", linkType: "page-ref" })
+  })
+
+  it("extracts ((block-id)) references", () => {
+    const links = extractLinks("ref ((abc123)) here")
+    expect(links).toHaveLength(1)
+    expect(links[0]).toMatchObject({ target: "abc123", linkType: "block-ref" })
+  })
+
+  it("extracts #tag references", () => {
+    const links = extractLinks("tagged #project-alpha here")
+    expect(links).toHaveLength(1)
+    expect(links[0]).toMatchObject({ target: "project-alpha", linkType: "tag" })
+  })
+
+  it("lowercases tag targets", () => {
+    const links = extractLinks("#Project-Alpha")
+    expect(links[0]!.target).toBe("project-alpha")
+  })
+
+  it("extracts multiple link types from mixed content", () => {
+    const content = "Page [[Foo]] and ((id-123)) and #tag"
+    const links = extractLinks(content)
+    expect(links).toHaveLength(3)
+    expect(links.map((l) => l.linkType).sort()).toEqual(["block-ref", "page-ref", "tag"])
+  })
+
+  it("records correct offset positions", () => {
+    const content = "text [[Foo]] text"
+    const links = extractLinks(content)
+    expect(links[0]!.offset).toBe(5) // [[Foo]] starts at position 5
+  })
+
+  it("handles content with no links", () => {
+    expect(extractLinks("plain text")).toHaveLength(0)
+  })
+})

+ 61 - 0
apps/tauri/src/lib/parse.ts

@@ -0,0 +1,61 @@
+/** Simple content hash for change detection (djb2 variant, base-36 encoded). */
+export function contentHash(content: string): string {
+  let hash = 0
+  for (let i = 0; i < content.length; i++) {
+    const chr = content.charCodeAt(i)
+    hash = ((hash << 5) - hash) + chr
+    hash |= 0
+  }
+  return hash.toString(36)
+}
+
+/**
+ * Classify a block's type from its first line content.
+ * Matches the block_type CHECK constraint in the schema.
+ */
+export function detectBlockType(content: string): string {
+  const first = content.trimStart()
+  if (/^#{1,6}\s/.test(first)) return "heading"
+  if (/^TODO\s|^DOING\s|^DONE\s|^LATER\s|^NOW\s|^WAITING\s|^CANCELLED\s/i.test(first)) return "task"
+  if (/^>\s*\[!/.test(first)) return "callout"
+  if (/^>\s/.test(first)) return "blockquote"
+  if (/^```/.test(first)) return "code"
+  if (/^\|.+\|/.test(first)) return "table"
+  if (/^\$\$/.test(first)) return "math"
+  if (/^\w+::\s/.test(first)) return "property"
+  return "paragraph"
+}
+
+/** Extract the first `# Heading 1` from markdown content. */
+export function extractTitle(content: string): string {
+  const match = content.match(/^# (.+)/m)
+  return match ? match[1]!.trim() : ""
+}
+
+export interface ExtractedLink {
+  target: string
+  linkType: "page-ref" | "block-ref" | "tag"
+  offset: number
+}
+
+/**
+ * Extract [[Page Name]], ((block-id)), and #tag references from block content.
+ * Returns target text, link type, and character offset within the block.
+ */
+export function extractLinks(content: string): ExtractedLink[] {
+  const links: ExtractedLink[] = []
+  const pageRefRe = /\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g
+  let m: RegExpExecArray | null
+  while ((m = pageRefRe.exec(content)) !== null) {
+    links.push({ target: m[1]!, linkType: "page-ref", offset: m.index })
+  }
+  const blockRefRe = /\(\(([a-zA-Z0-9_-]+)\)\)/g
+  while ((m = blockRefRe.exec(content)) !== null) {
+    links.push({ target: m[1]!, linkType: "block-ref", offset: m.index })
+  }
+  const tagRe = /(?:\s|^)#([a-zA-Z0-9_-]+)/g
+  while ((m = tagRe.exec(content)) !== null) {
+    links.push({ target: m[1]!.toLowerCase(), linkType: "tag", offset: m.index + 1 })
+  }
+  return links
+}

+ 80 - 0
apps/tauri/src/lib/schema.test.ts

@@ -0,0 +1,80 @@
+import { describe, it, expect, beforeAll } from "vitest"
+import Database from "better-sqlite3"
+import { SCHEMA_SQL } from "./schema"
+
+let db: Database.Database
+
+beforeAll(() => {
+  db = new Database(":memory:")
+  db.exec(SCHEMA_SQL)
+})
+
+describe("schema integrity", () => {
+  it("creates pages table", () => {
+    const result = db.prepare(
+      "SELECT name FROM sqlite_master WHERE type='table' AND name='pages'",
+    ).get() as { name: string } | undefined
+    expect(result?.name).toBe("pages")
+  })
+
+  it("creates blocks table", () => {
+    const result = db.prepare(
+      "SELECT name FROM sqlite_master WHERE type='table' AND name='blocks'",
+    ).get() as { name: string } | undefined
+    expect(result?.name).toBe("blocks")
+  })
+
+  it("creates links table", () => {
+    const result = db.prepare(
+      "SELECT name FROM sqlite_master WHERE type='table' AND name='links'",
+    ).get() as { name: string } | undefined
+    expect(result?.name).toBe("links")
+  })
+
+  it("creates blocks_fts virtual table", () => {
+    const result = db.prepare(
+      "SELECT name FROM sqlite_master WHERE type='table' AND name='blocks_fts'",
+    ).get() as { name: string } | undefined
+    expect(result?.name).toBe("blocks_fts")
+  })
+
+  it("enforces pages.path primary key", () => {
+    db.prepare("INSERT INTO pages (path, title, modified_at) VALUES ('a.md', 'A', 1000)").run()
+    expect(() => {
+      db.prepare("INSERT INTO pages (path, title, modified_at) VALUES ('a.md', 'A2', 2000)").run()
+    }).toThrow()
+  })
+
+  it("creates all expected indices", () => {
+    const indices = db.prepare(
+      "SELECT name FROM sqlite_master WHERE type='index' AND name LIKE 'idx_%'",
+    ).all() as { name: string }[]
+    const names = indices.map((i) => i.name).sort()
+    expect(names).toContain("idx_blocks_page")
+    expect(names).toContain("idx_links_target")
+    expect(names).toContain("idx_links_source")
+    expect(names).toContain("idx_links_tag")
+  })
+
+  it("enforces link_type CHECK constraint", () => {
+    db.prepare(
+      "INSERT INTO pages (path, title, modified_at) VALUES ('p.md', 'P', 1000)",
+    ).run()
+    db.prepare(
+      "INSERT INTO blocks (id, page_path, content, modified_at) VALUES ('b1', 'p.md', 'c', 1000)",
+    ).run()
+    expect(() => {
+      db.prepare(
+        "INSERT INTO links (source_block_id, source_page_path, target, link_type) VALUES ('b1', 'p.md', 'x', 'invalid-type')",
+      ).run()
+    }).toThrow()
+  })
+
+  it("enforces blocks.block_type CHECK constraint", () => {
+    expect(() => {
+      db.prepare(
+        "INSERT INTO blocks (id, page_path, block_type, modified_at) VALUES ('b2', 'p.md', 'invalid-type', 1000)",
+      ).run()
+    }).toThrow()
+  })
+})

+ 65 - 0
apps/tauri/src/lib/schema.ts

@@ -0,0 +1,65 @@
+/**
+ * SQLite index schema for the workspace index.db.
+ * Tables are created with IF NOT EXISTS so this is safe to run on every load.
+ */
+export const SCHEMA_SQL = `
+CREATE TABLE IF NOT EXISTS pages (
+    path TEXT PRIMARY KEY,
+    title TEXT NOT NULL DEFAULT '',
+    modified_at INTEGER NOT NULL,
+    word_count INTEGER NOT NULL DEFAULT 0
+);
+
+CREATE TABLE IF NOT EXISTS blocks (
+    id TEXT PRIMARY KEY,
+    page_path TEXT NOT NULL REFERENCES pages(path) ON DELETE CASCADE,
+    content TEXT NOT NULL DEFAULT '',
+    depth INTEGER NOT NULL DEFAULT 0,
+    block_type TEXT CHECK(block_type IN (
+        'paragraph', 'heading', 'task', 'callout',
+        'blockquote', 'code', 'table', 'math', 'property'
+    )),
+    has_id_stamp INTEGER NOT NULL DEFAULT 0,
+    content_hash TEXT NOT NULL DEFAULT '',
+    modified_at INTEGER NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS links (
+    id INTEGER PRIMARY KEY AUTOINCREMENT,
+    source_block_id TEXT NOT NULL REFERENCES blocks(id) ON DELETE CASCADE,
+    source_page_path TEXT NOT NULL REFERENCES pages(path) ON DELETE CASCADE,
+    target TEXT NOT NULL,
+    link_type TEXT NOT NULL CHECK(link_type IN ('page-ref', 'block-ref', 'tag')),
+    offset_in_block INTEGER NOT NULL DEFAULT 0
+);
+
+CREATE VIRTUAL TABLE IF NOT EXISTS blocks_fts USING fts5(
+    content,
+    block_id UNINDEXED,
+    page_path UNINDEXED,
+    content='blocks',
+    content_rowid='rowid'
+);
+
+CREATE TRIGGER IF NOT EXISTS blocks_ai AFTER INSERT ON blocks BEGIN
+    INSERT INTO blocks_fts(rowid, content, block_id, page_path)
+    VALUES (new.rowid, new.content, new.id, new.page_path);
+END;
+
+CREATE TRIGGER IF NOT EXISTS blocks_ad AFTER DELETE ON blocks BEGIN
+    INSERT INTO blocks_fts(blocks_fts, rowid, content, block_id, page_path)
+    VALUES ('delete', old.rowid, old.content, old.id, old.page_path);
+END;
+
+CREATE TRIGGER IF NOT EXISTS blocks_au AFTER UPDATE ON blocks BEGIN
+    INSERT INTO blocks_fts(blocks_fts, rowid, content, block_id, page_path)
+    VALUES ('delete', old.rowid, old.content, old.id, old.page_path);
+    INSERT INTO blocks_fts(rowid, content, block_id, page_path)
+    VALUES (new.rowid, new.content, new.id, new.page_path);
+END;
+
+CREATE INDEX IF NOT EXISTS idx_blocks_page ON blocks(page_path, depth);
+CREATE INDEX IF NOT EXISTS idx_links_target ON links(target, link_type);
+CREATE INDEX IF NOT EXISTS idx_links_source ON links(source_block_id);
+CREATE INDEX IF NOT EXISTS idx_links_tag ON links(target) WHERE link_type = 'tag';
+`

+ 184 - 0
apps/tauri/src/stores/workspace.ts

@@ -0,0 +1,184 @@
+/**
+ * Workspace Pinia store.
+ * Single source of truth for workspace state across all components.
+ */
+import { defineStore } from "pinia"
+import { open } from "@tauri-apps/plugin-dialog"
+import { listen } from "@tauri-apps/api/event"
+import { invoke } from "@tauri-apps/api/core"
+import type Database from "@tauri-apps/plugin-sql"
+import { useFileSystem } from "~/composables/useFileSystem"
+import { useSettings } from "~/composables/useSettings"
+import { initIndex, fullReindex, indexFile, removeFile } from "~/lib/indexer"
+
+// Track files the app wrote itself, to avoid re-indexing on our own writes
+const inFlightWrites = new Set<string>()
+
+export function markInFlight(path: string) {
+  inFlightWrites.add(path)
+  setTimeout(() => inFlightWrites.delete(path), 500)
+}
+
+export interface WorkspaceFile {
+  path: string
+  name: string
+  isJournal: boolean
+  date?: string
+}
+
+export const useWorkspaceStore = defineStore("workspace", () => {
+  const workspacePath = ref<string | null>(null)
+  const files = ref<WorkspaceFile[]>([])
+  const currentFilePath = ref<string | null>(null)
+  const loading = ref(false)
+  const db = ref<Database | null>(null)
+
+  const { readFile, writeFile, listDirectory, createDirectory } = useFileSystem()
+  const settings = useSettings()
+
+  const journals = computed(() => files.value.filter((f) => f.isJournal))
+  const pages = computed(() => files.value.filter((f) => !f.isJournal))
+
+  async function initializeIndex(path: string) {
+    db.value = await initIndex(`${path}/index.db`)
+    await fullReindex(
+      { listDirectory, readFile, workspacePath: path },
+      db.value,
+    )
+
+    // Start file watcher
+    try {
+      await invoke("start_watcher", { workspacePath: path })
+    } catch (e) {
+      console.warn("[workspace] failed to start file watcher:", e)
+    }
+
+    // Listen for file changes from the watcher
+    const unlisten = await listen<{ path: string; kind: string }>(
+      "fs-watch:change",
+      async (event) => {
+        const { path: changedPath, kind } = event.payload
+
+        // Ignore events from files we wrote ourselves
+        if (inFlightWrites.has(changedPath) || inFlightWrites.has(changedPath.replace("/./", "/"))) {
+          return
+        }
+
+        if (!db.value) return
+
+        try {
+          if (kind === "deleted") {
+            await removeFile(db.value, changedPath)
+          } else {
+            const content = await readFile(changedPath)
+            await indexFile(db.value, changedPath, content)
+          }
+          await scanFiles()
+        } catch (e) {
+          console.warn("[workspace] watcher: failed to re-index", changedPath, e)
+        }
+      },
+    )
+  }
+
+  async function initialize(): Promise<boolean> {
+    const saved = await settings.get<string>("workspacePath")
+    if (saved) {
+      workspacePath.value = saved
+      const sessionFile = await settings.get<string>("currentFilePath")
+      if (sessionFile) currentFilePath.value = sessionFile
+      await scanFiles()
+      await initializeIndex(saved)
+      return true
+    }
+    return false
+  }
+
+  async function pickWorkspace(): Promise<string | null> {
+    const selected = await open({
+      directory: true,
+      multiple: false,
+      title: "Select your workspace directory",
+    })
+    if (!selected) return null
+
+    workspacePath.value = selected
+    await settings.set("workspacePath", selected)
+    await ensureDirectories()
+    await scanFiles()
+    await initializeIndex(selected)
+    return selected
+  }
+
+  async function ensureDirectories() {
+    if (!workspacePath.value) return
+    await createDirectory(`${workspacePath.value}/journals`)
+    await createDirectory(`${workspacePath.value}/pages`)
+    await createDirectory(`${workspacePath.value}/templates`)
+  }
+
+  async function scanFiles() {
+    if (!workspacePath.value) return
+    loading.value = true
+    try {
+      const allFiles = await listDirectory(workspacePath.value)
+      const markdownFiles = allFiles.filter((f) => f.endsWith(".md"))
+      files.value = markdownFiles.map((p) => {
+        const name = p.split("/").pop() ?? p
+        const isJournal = !!name.match(/^\d{4}-\d{2}-\d{2}\.md$/)
+        return { path: p, name, isJournal, date: isJournal ? name.replace(".md", "") : undefined }
+      })
+    } finally {
+      loading.value = false
+    }
+  }
+
+  async function loadFile(relativePath: string): Promise<string> {
+    if (!workspacePath.value) return ""
+    return await readFile(`${workspacePath.value}/${relativePath}`)
+  }
+
+  async function saveFile(relativePath: string, content: string): Promise<void> {
+    if (!workspacePath.value) return
+    await writeFile(`${workspacePath.value}/${relativePath}`, content)
+  }
+
+  async function loadSavedFile(): Promise<string | null> {
+    if (!currentFilePath.value || !workspacePath.value) return null
+    try {
+      return await readFile(currentFilePath.value)
+    } catch {
+      currentFilePath.value = null
+      return null
+    }
+  }
+
+  async function saveCurrentFile(content: string): Promise<void> {
+    if (!currentFilePath.value || !workspacePath.value) return
+    await writeFile(currentFilePath.value, content)
+  }
+
+  function openFile(relativePath: string) {
+    if (!workspacePath.value) return
+    currentFilePath.value = `${workspacePath.value}/${relativePath}`
+    settings.set("currentFilePath", currentFilePath.value)
+  }
+
+  return {
+    workspacePath,
+    files,
+    journals,
+    pages,
+    currentFilePath,
+    loading,
+    db,
+    initialize,
+    pickWorkspace,
+    scanFiles,
+    loadFile,
+    saveFile,
+    loadSavedFile,
+    saveCurrentFile,
+    openFile,
+  }
+})

+ 16 - 0
apps/tauri/vitest.config.ts

@@ -0,0 +1,16 @@
+import { resolve } from "node:path"
+import { defineConfig } from "vitest/config"
+
+const EDITOR_ROOT = resolve(__dirname, "../../packages/editor")
+
+export default defineConfig({
+  test: {
+    include: ["src/**/*.test.ts"],
+  },
+  resolve: {
+    alias: [
+      { find: /^@\//, replacement: `${EDITOR_ROOT}/src/` },
+      { find: "@enesis/editor", replacement: `${EDITOR_ROOT}/src/index.ts` },
+    ],
+  },
+})

+ 1 - 0
packages/editor/src/index.ts

@@ -11,6 +11,7 @@ export type { FocusTarget } from "@/composables/useFocusRegistry"
 export {
   extractEmbeddedId,
   generateBlockId,
+  splitMarkdownIntoBlocks,
   stampBlockId,
 } from "@/lib/block-parser"
 export type { FormattingHandlers } from "@/lib/formatting"

+ 291 - 37
pnpm-lock.yaml

@@ -31,7 +31,7 @@ importers:
         version: 1.2.83
       '@nuxt/ui':
         specifier: ^4.7.1
-        version: 4.7.1(@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])
+        version: 4.7.1(@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])
       '@tiptap/core':
         specifier: ^3.23.4
         version: 3.23.5(@tiptap/[email protected])
@@ -83,22 +83,34 @@ importers:
         version: 5.2.9
       '@nuxt/ui':
         specifier: ^4.7.1
-        version: 4.7.1(@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])
+        version: 4.7.1(@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])
       '@nuxtjs/color-mode':
         specifier: ^4.0.1
         version: 4.0.1([email protected])
+      '@pinia/nuxt':
+        specifier: ^0.11.3
+        version: 0.11.3([email protected])([email protected]([email protected])([email protected]([email protected])))
       '@tauri-apps/api':
         specifier: ^2
         version: 2.11.1
+      '@tauri-apps/plugin-dialog':
+        specifier: ^2.7.1
+        version: 2.7.1
       '@tauri-apps/plugin-fs':
         specifier: ~2
         version: 2.5.1
       '@tauri-apps/plugin-opener':
         specifier: ^2
         version: 2.5.4
+      '@tauri-apps/plugin-sql':
+        specifier: ^2.4.0
+        version: 2.4.0
+      '@tauri-apps/plugin-store':
+        specifier: ^2.4.3
+        version: 2.4.3
       nuxt:
         specifier: ^4.4.8
-        version: 4.4.8(@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected])
+        version: 4.4.8(@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected])
       vue:
         specifier: ^3.5.34
         version: 3.5.34([email protected])
@@ -109,9 +121,18 @@ importers:
       '@tauri-apps/cli':
         specifier: ^2
         version: 2.11.2
+      '@types/better-sqlite3':
+        specifier: ^7.6.13
+        version: 7.6.13
+      better-sqlite3:
+        specifier: ^12.11.1
+        version: 12.11.1
       typescript:
         specifier: ~6.0.2
         version: 6.0.3
+      vitest:
+        specifier: ^4.1.7
+        version: 4.1.7(@types/[email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       vue-tsc:
         specifier: ^3.2.8
         version: 3.3.1([email protected])
@@ -199,7 +220,7 @@ importers:
     devDependencies:
       '@nuxt/ui':
         specifier: ^4.7.1
-        version: 4.7.1(@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])
+        version: 4.7.1(@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])
       '@playwright/test':
         specifier: ^1.61.0
         version: 1.61.0
@@ -1795,6 +1816,11 @@ packages:
     resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
     engines: {node: '>= 10.0.0'}
 
+  '@pinia/[email protected]':
+    resolution: {integrity: sha512-7WVNHpWx4qAEzOlnyrRC88kYrwnlR/PrThWT0XI1dSNyUAXu/KBv9oR37uCgYkZroqP5jn8DfzbkNF3BtKvE9w==}
+    peerDependencies:
+      pinia: ^3.0.4
+
   '@pkgjs/[email protected]':
     resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
     engines: {node: '>=14'}
@@ -2363,12 +2389,21 @@ packages:
     engines: {node: '>= 10'}
     hasBin: true
 
+  '@tauri-apps/[email protected]':
+    resolution: {integrity: sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==}
+
   '@tauri-apps/[email protected]':
     resolution: {integrity: sha512-9Lz+Jopp6QyeEWhlpkMx4R/+P9HgR+AVAI4vOZhlT8Xaymtz8iVI/Ov984/XTqgJz/5gz5NretqPB/XEMS3NhQ==}
 
   '@tauri-apps/[email protected]':
     resolution: {integrity: sha512-1HnPkb+AmgO29HBazm4uPLKB+r7zzcTBW1d0fyYp1uP+jwtpoiNDGKMMzz58SFp49nOIrxdE3aUJtT57lfO9CQ==}
 
+  '@tauri-apps/[email protected]':
+    resolution: {integrity: sha512-SIICc5JlnK6OrBZzOw7MmhXHPlmASpt5zLWIu10WW4kLr5cDYOXHdV2MoCgYQkgZLQfyBYgF3SQa5XCisUiQkw==}
+
+  '@tauri-apps/[email protected]':
+    resolution: {integrity: sha512-9LWPj9yMphRi9czEtUv87XHbl1b6xgd9EXpPrUnq6nG7+nbtoF84d4Kwz9xhAv/Hf30sr58pq7EOlyI936y8qw==}
+
   '@tiptap/[email protected]':
     resolution: {integrity: sha512-657Xqcgf1IYWLkAmRDJKNSGdoS1AHJEgK6zHWHFJERQGIqHnwC7Fz7nvWs/NQhQVBkclQd0ERRdTCZ3XwRc1+g==}
     peerDependencies:
@@ -2592,6 +2627,9 @@ packages:
   '@tybys/[email protected]':
     resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
 
+  '@types/[email protected]':
+    resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
+
   '@types/[email protected]':
     resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
 
@@ -3061,6 +3099,10 @@ packages:
     engines: {node: '>=6.0.0'}
     hasBin: true
 
+  [email protected]:
+    resolution: {integrity: sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==}
+    engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x}
+
   [email protected]:
     resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
 
@@ -3077,6 +3119,9 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==}
 
+  [email protected]:
+    resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+
   [email protected]:
     resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==}
     engines: {node: '>=18'}
@@ -3110,6 +3155,9 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
 
+  [email protected]:
+    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+
   [email protected]:
     resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
 
@@ -3155,6 +3203,9 @@ packages:
     resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
     engines: {node: '>= 20.19.0'}
 
+  [email protected]:
+    resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+
   [email protected]:
     resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
     engines: {node: '>=18'}
@@ -3375,6 +3426,14 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
 
+  [email protected]:
+    resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+    engines: {node: '>=10'}
+
+  [email protected]:
+    resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+    engines: {node: '>=4.0.0'}
+
   [email protected]:
     resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
     engines: {node: '>=0.10.0'}
@@ -3515,6 +3574,9 @@ packages:
     resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
     engines: {node: '>= 0.8'}
 
+  [email protected]:
+    resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
+
   [email protected]:
     resolution: {integrity: sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==}
     engines: {node: '>=10.13.0'}
@@ -3606,6 +3668,10 @@ packages:
     resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
     engines: {node: '>=16.17'}
 
+  [email protected]:
+    resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+    engines: {node: '>=6'}
+
   [email protected]:
     resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
     engines: {node: '>=12.0.0'}
@@ -3735,6 +3801,9 @@ packages:
     resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
     engines: {node: '>= 0.8'}
 
+  [email protected]:
+    resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+
   [email protected]:
     resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -3792,6 +3861,9 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==}
 
+  [email protected]:
+    resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+
   [email protected]:
     resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
     engines: {node: '>= 6'}
@@ -4268,6 +4340,10 @@ packages:
     resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
     engines: {node: '>=12'}
 
+  [email protected]:
+    resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+    engines: {node: '>=10'}
+
   [email protected]:
     resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
     engines: {node: 18 || 20 || >=22}
@@ -4294,6 +4370,9 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
 
+  [email protected]:
+    resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+
   [email protected]:
     resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
 
@@ -4344,6 +4423,9 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-Kv2JYYiCzt16Kt5QwAc9BFG89xfPNBx+oQL4GQXD9nLqPkZBiNaqaCWtwnbk/q7UVsTYevvM1b0UF8zmEI4pCg==}
 
+  [email protected]:
+    resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
+
   [email protected]:
     resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
     engines: {node: '>= 0.6'}
@@ -4361,6 +4443,10 @@ packages:
       xml2js:
         optional: true
 
+  [email protected]:
+    resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==}
+    engines: {node: '>=10'}
+
   [email protected]:
     resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
 
@@ -4781,6 +4867,12 @@ packages:
     resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
     engines: {node: '>=20'}
 
+  [email protected]:
+    resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==}
+    engines: {node: '>=10'}
+    deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available.
+    hasBin: true
+
   [email protected]:
     resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==}
     engines: {node: '>=20'}
@@ -4841,6 +4933,9 @@ packages:
     resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
     engines: {node: '>= 0.10'}
 
+  [email protected]:
+    resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==}
+
   [email protected]:
     resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
     engines: {node: '>=6'}
@@ -4869,9 +4964,17 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==}
 
+  [email protected]:
+    resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+    hasBin: true
+
   [email protected]:
     resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
 
+  [email protected]:
+    resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+    engines: {node: '>= 6'}
+
   [email protected]:
     resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -5063,6 +5166,12 @@ packages:
     resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
     engines: {node: '>=14'}
 
+  [email protected]:
+    resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+
+  [email protected]:
+    resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+
   [email protected]:
     resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==}
 
@@ -5151,6 +5260,10 @@ packages:
     resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
     engines: {node: '>=12'}
 
+  [email protected]:
+    resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+    engines: {node: '>=0.10.0'}
+
   [email protected]:
     resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
 
@@ -5213,6 +5326,13 @@ packages:
     resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
     engines: {node: '>=6'}
 
+  [email protected]:
+    resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==}
+
+  [email protected]:
+    resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+    engines: {node: '>=6'}
+
   [email protected]:
     resolution: {integrity: sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==}
 
@@ -5301,6 +5421,9 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
 
+  [email protected]:
+    resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+
   [email protected]:
     resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==}
     engines: {node: '>=20'}
@@ -7064,13 +7187,13 @@ snapshots:
       - utf-8-validate
       - vue
 
-  '@nuxt/[email protected]([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))':
+  '@nuxt/[email protected]([email protected]([email protected]))([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))':
     dependencies:
       '@nuxt/devtools-kit': 3.2.4([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       '@nuxt/kit': 4.4.6([email protected])
       consola: 3.4.2
       defu: 6.1.7
-      fontless: 0.2.1([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
+      fontless: 0.2.1([email protected]([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       h3: 1.15.11
       magic-regexp: 0.10.0
       ofetch: 1.5.1
@@ -7080,7 +7203,7 @@ snapshots:
       ufo: 1.6.4
       unifont: 0.7.4
       unplugin: 3.0.0
-      unstorage: 1.17.5([email protected])([email protected])
+      unstorage: 1.17.5([email protected]([email protected]))([email protected])
     transitivePeerDependencies:
       - '@azure/app-configuration'
       - '@azure/cosmos'
@@ -7104,13 +7227,13 @@ snapshots:
       - uploadthing
       - vite
 
-  '@nuxt/[email protected]([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))':
+  '@nuxt/[email protected]([email protected]([email protected]))([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))':
     dependencies:
       '@nuxt/devtools-kit': 3.2.4([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       '@nuxt/kit': 4.4.6([email protected])
       consola: 3.4.2
       defu: 6.1.7
-      fontless: 0.2.1([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
+      fontless: 0.2.1([email protected]([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       h3: 1.15.11
       magic-regexp: 0.10.0
       ofetch: 1.5.1
@@ -7120,7 +7243,7 @@ snapshots:
       ufo: 1.6.4
       unifont: 0.7.4
       unplugin: 3.0.0
-      unstorage: 1.17.5([email protected])([email protected])
+      unstorage: 1.17.5([email protected]([email protected]))([email protected])
     transitivePeerDependencies:
       - '@azure/app-configuration'
       - '@azure/cosmos'
@@ -7262,7 +7385,7 @@ snapshots:
     transitivePeerDependencies:
       - magicast
 
-  '@nuxt/[email protected](@babel/[email protected](@babel/[email protected]))([email protected])([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])':
+  '@nuxt/[email protected](@babel/[email protected](@babel/[email protected]))([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])':
     dependencies:
       '@nuxt/devalue': 2.0.2
       '@nuxt/kit': 4.4.8([email protected])
@@ -7279,8 +7402,8 @@ snapshots:
       impound: 1.1.5
       klona: 2.0.6
       mocked-exports: 0.1.1
-      nitropack: 2.13.4([email protected])([email protected])
-      nuxt: 4.4.8(@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected])
+      nitropack: 2.13.4([email protected])([email protected])([email protected])
+      nuxt: 4.4.8(@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected])
       nypm: 0.6.7
       ohash: 2.0.11
       pathe: 2.0.3
@@ -7288,7 +7411,7 @@ snapshots:
       std-env: 4.1.0
       ufo: 1.6.4
       unctx: 2.5.0
-      unstorage: 1.17.5([email protected])([email protected])
+      unstorage: 1.17.5([email protected]([email protected]))([email protected])
       vue: 3.5.38([email protected])
       vue-bundle-renderer: 2.2.0
       vue-devtools-stub: 0.1.0
@@ -7356,11 +7479,11 @@ snapshots:
       rc9: 3.0.1
       std-env: 4.1.0
 
-  '@nuxt/[email protected](@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])':
+  '@nuxt/[email protected](@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])':
     dependencies:
       '@floating-ui/dom': 1.7.6
       '@iconify/vue': 5.0.1([email protected]([email protected]))
-      '@nuxt/fonts': 0.14.0([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
+      '@nuxt/fonts': 0.14.0([email protected]([email protected]))([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       '@nuxt/icon': 2.2.2([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))
       '@nuxt/kit': 4.4.6([email protected])
       '@nuxt/schema': 4.4.6
@@ -7470,11 +7593,11 @@ snapshots:
       - vue
       - yjs
 
-  '@nuxt/[email protected](@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])':
+  '@nuxt/[email protected](@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])':
     dependencies:
       '@floating-ui/dom': 1.7.6
       '@iconify/vue': 5.0.1([email protected]([email protected]))
-      '@nuxt/fonts': 0.14.0([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
+      '@nuxt/fonts': 0.14.0([email protected]([email protected]))([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       '@nuxt/icon': 2.2.2([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))
       '@nuxt/kit': 4.4.6([email protected])
       '@nuxt/schema': 4.4.6
@@ -7584,11 +7707,11 @@ snapshots:
       - vue
       - yjs
 
-  '@nuxt/[email protected](@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])':
+  '@nuxt/[email protected](@internationalized/[email protected])(@internationalized/[email protected])(@tiptap/[email protected](@tiptap/[email protected](@tiptap/[email protected]))(@tiptap/[email protected]))(@tiptap/[email protected]([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected](@vue/[email protected])([email protected]([email protected])([email protected]([email protected])))([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected])))([email protected]([email protected]))([email protected])([email protected])':
     dependencies:
       '@floating-ui/dom': 1.7.6
       '@iconify/vue': 5.0.1([email protected]([email protected]))
-      '@nuxt/fonts': 0.14.0([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
+      '@nuxt/fonts': 0.14.0([email protected]([email protected]))([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))
       '@nuxt/icon': 2.2.2([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))
       '@nuxt/kit': 4.4.6([email protected])
       '@nuxt/schema': 4.4.6
@@ -7698,7 +7821,7 @@ snapshots:
       - vue
       - yjs
 
-  '@nuxt/[email protected](@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@types/[email protected])([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]))([email protected])':
+  '@nuxt/[email protected](@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@types/[email protected])([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]))([email protected])':
     dependencies:
       '@nuxt/kit': 4.4.8([email protected])
       '@rollup/plugin-replace': 6.0.3([email protected])
@@ -7716,7 +7839,7 @@ snapshots:
       magic-string: 0.30.21
       mlly: 1.8.2
       mocked-exports: 0.1.1
-      nuxt: 4.4.8(@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected])
+      nuxt: 4.4.8(@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected])
       nypm: 0.6.7
       pathe: 2.0.3
       pkg-types: 2.3.1
@@ -8039,6 +8162,13 @@ snapshots:
       '@parcel/watcher-win32-ia32': 2.5.6
       '@parcel/watcher-win32-x64': 2.5.6
 
+  '@pinia/[email protected]([email protected])([email protected]([email protected])([email protected]([email protected])))':
+    dependencies:
+      '@nuxt/kit': 4.4.8([email protected])
+      pinia: 3.0.4([email protected])([email protected]([email protected]))
+    transitivePeerDependencies:
+      - magicast
+
   '@pkgjs/[email protected]':
     optional: true
 
@@ -8432,6 +8562,10 @@ snapshots:
       '@tauri-apps/cli-win32-ia32-msvc': 2.11.2
       '@tauri-apps/cli-win32-x64-msvc': 2.11.2
 
+  '@tauri-apps/[email protected]':
+    dependencies:
+      '@tauri-apps/api': 2.11.1
+
   '@tauri-apps/[email protected]':
     dependencies:
       '@tauri-apps/api': 2.11.1
@@ -8440,6 +8574,14 @@ snapshots:
     dependencies:
       '@tauri-apps/api': 2.11.1
 
+  '@tauri-apps/[email protected]':
+    dependencies:
+      '@tauri-apps/api': 2.11.1
+
+  '@tauri-apps/[email protected]':
+    dependencies:
+      '@tauri-apps/api': 2.11.1
+
   '@tiptap/[email protected](@tiptap/[email protected])':
     dependencies:
       '@tiptap/pm': 3.23.5
@@ -8671,6 +8813,10 @@ snapshots:
       tslib: 2.8.1
     optional: true
 
+  '@types/[email protected]':
+    dependencies:
+      '@types/node': 25.9.1
+
   '@types/[email protected]':
     dependencies:
       '@types/deep-eql': 4.0.2
@@ -9243,6 +9389,11 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      bindings: 1.5.0
+      prebuild-install: 7.1.3
+
   [email protected]:
     dependencies:
       require-from-string: 2.0.2
@@ -9257,6 +9408,12 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      buffer: 5.7.1
+      inherits: 2.0.4
+      readable-stream: 3.6.2
+
   [email protected]:
     dependencies:
       bytes: 3.1.2
@@ -9299,6 +9456,11 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+
   [email protected]:
     dependencies:
       base64-js: 1.5.1
@@ -9352,6 +9514,8 @@ snapshots:
     dependencies:
       readdirp: 5.0.0
 
+  [email protected]: {}
+
   [email protected]: {}
 
   [email protected]:
@@ -9537,7 +9701,9 @@ snapshots:
     transitivePeerDependencies:
       - '@noble/hashes'
 
-  [email protected]: {}
+  [email protected]([email protected]):
+    optionalDependencies:
+      better-sqlite3: 12.11.1
 
   [email protected]:
     dependencies:
@@ -9545,6 +9711,12 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      mimic-response: 3.1.0
+
+  [email protected]: {}
+
   [email protected]: {}
 
   [email protected]: {}
@@ -9660,6 +9832,10 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      once: 1.4.0
+
   [email protected]:
     dependencies:
       graceful-fs: 4.2.11
@@ -9785,6 +9961,8 @@ snapshots:
       signal-exit: 4.1.0
       strip-final-newline: 3.0.0
 
+  [email protected]: {}
+
   [email protected]: {}
 
   [email protected]([email protected]):
@@ -9897,7 +10075,7 @@ snapshots:
     dependencies:
       tiny-inflate: 1.0.3
 
-  [email protected]([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])):
+  [email protected]([email protected]([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])):
     dependencies:
       consola: 3.4.2
       css-tree: 3.2.1
@@ -9911,7 +10089,7 @@ snapshots:
       pathe: 2.0.3
       ufo: 1.6.4
       unifont: 0.7.4
-      unstorage: 1.17.5([email protected])([email protected])
+      unstorage: 1.17.5([email protected]([email protected]))([email protected])
     optionalDependencies:
       vite: 8.0.13(@types/[email protected])([email protected])([email protected])([email protected])([email protected])
     transitivePeerDependencies:
@@ -9935,7 +10113,7 @@ snapshots:
       - ioredis
       - uploadthing
 
-  [email protected]([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])):
+  [email protected]([email protected]([email protected]))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])):
     dependencies:
       consola: 3.4.2
       css-tree: 3.2.1
@@ -9949,7 +10127,7 @@ snapshots:
       pathe: 2.0.3
       ufo: 1.6.4
       unifont: 0.7.4
-      unstorage: 1.17.5([email protected])([email protected])
+      unstorage: 1.17.5([email protected]([email protected]))([email protected])
     optionalDependencies:
       vite: 8.0.13(@types/[email protected])([email protected])([email protected])([email protected])([email protected])
     transitivePeerDependencies:
@@ -9996,6 +10174,8 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]:
     optional: true
 
@@ -10047,6 +10227,8 @@ snapshots:
     dependencies:
       git-up: 8.1.1
 
+  [email protected]: {}
+
   [email protected]:
     dependencies:
       is-glob: 4.0.3
@@ -10497,6 +10679,8 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]:
     dependencies:
       brace-expansion: 5.0.6
@@ -10519,6 +10703,8 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]:
     dependencies:
       acorn: 8.16.0
@@ -10576,11 +10762,13 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]: {}
 
   [email protected]: {}
 
-  [email protected]([email protected])([email protected]):
+  [email protected]([email protected])([email protected])([email protected]):
     dependencies:
       '@cloudflare/kv-asset-handler': 0.4.2
       '@rollup/plugin-alias': 6.0.0([email protected])
@@ -10601,7 +10789,7 @@ snapshots:
       cookie-es: 2.0.1
       croner: 10.0.1
       crossws: 0.3.5
-      db0: 0.3.4
+      db0: 0.3.4([email protected])
       defu: 6.1.7
       destr: 2.0.5
       dot-prop: 10.1.0
@@ -10647,7 +10835,7 @@ snapshots:
       unenv: 2.0.0-rc.24
       unimport: 6.3.0([email protected])([email protected])
       unplugin-utils: 0.3.1
-      unstorage: 1.17.5([email protected])([email protected])
+      unstorage: 1.17.5([email protected]([email protected]))([email protected])
       untyped: 2.0.0
       unwasm: 0.5.3
       youch: 4.1.1
@@ -10684,6 +10872,10 @@ snapshots:
       - supports-color
       - uploadthing
 
+  [email protected]:
+    dependencies:
+      semver: 7.8.4
+
   [email protected]: {}
 
   [email protected]: {}
@@ -10723,16 +10915,16 @@ snapshots:
     dependencies:
       boolbase: 1.0.0
 
-  [email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]):
+  [email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]):
     dependencies:
       '@dxup/nuxt': 0.4.1([email protected])([email protected])
       '@nuxt/cli': 3.35.2(@nuxt/[email protected])([email protected])([email protected])
       '@nuxt/devtools': 3.2.4([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))
       '@nuxt/kit': 4.4.8([email protected])
-      '@nuxt/nitro-server': 4.4.8(@babel/[email protected](@babel/[email protected]))([email protected])([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])
+      '@nuxt/nitro-server': 4.4.8(@babel/[email protected](@babel/[email protected]))([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected])([email protected])
       '@nuxt/schema': 4.4.8
       '@nuxt/telemetry': 2.8.0(@nuxt/[email protected]([email protected]))
-      '@nuxt/vite-builder': 4.4.8(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@types/[email protected])([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]))([email protected])
+      '@nuxt/vite-builder': 4.4.8(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@types/[email protected])([email protected])([email protected])([email protected](@babel/[email protected](@babel/[email protected]))(@babel/[email protected](@babel/[email protected]))(@biomejs/[email protected])(@parcel/[email protected])(@types/[email protected])(@vue/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected])([email protected]([email protected])))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected]))([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected])([email protected]))([email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]))([email protected])
       '@unhead/vue': 2.1.15([email protected]([email protected]))
       '@vue/shared': 3.5.38
       chokidar: 5.0.0
@@ -11228,6 +11420,21 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      detect-libc: 2.1.2
+      expand-template: 2.0.3
+      github-from-package: 0.0.0
+      minimist: 1.2.8
+      mkdirp-classic: 0.5.3
+      napi-build-utils: 2.0.0
+      node-abi: 3.92.0
+      pump: 3.0.4
+      rc: 1.2.8
+      simple-get: 4.0.1
+      tar-fs: 2.1.4
+      tunnel-agent: 0.6.0
+
   [email protected]: {}
 
   [email protected]: {}
@@ -11318,6 +11525,11 @@ snapshots:
       forwarded: 0.2.0
       ipaddr.js: 1.9.1
 
+  [email protected]:
+    dependencies:
+      end-of-stream: 1.4.5
+      once: 1.4.0
+
   [email protected]: {}
 
   [email protected]:
@@ -11344,6 +11556,13 @@ snapshots:
       defu: 6.1.7
       destr: 2.0.5
 
+  [email protected]:
+    dependencies:
+      deep-extend: 0.6.0
+      ini: 1.3.8
+      minimist: 1.2.8
+      strip-json-comments: 2.0.1
+
   [email protected]:
     dependencies:
       core-util-is: 1.0.3
@@ -11354,6 +11573,12 @@ snapshots:
       string_decoder: 1.1.1
       util-deprecate: 1.0.2
 
+  [email protected]:
+    dependencies:
+      inherits: 2.0.4
+      string_decoder: 1.3.0
+      util-deprecate: 1.0.2
+
   [email protected]:
     dependencies:
       abort-controller: 3.0.0
@@ -11622,6 +11847,14 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
+  [email protected]:
+    dependencies:
+      decompress-response: 6.0.0
+      once: 1.4.0
+      simple-concat: 1.0.1
+
   [email protected]:
     dependencies:
       '@kwsites/file-exists': 1.1.1
@@ -11712,6 +11945,8 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]:
     dependencies:
       js-tokens: 9.0.1
@@ -11764,6 +11999,21 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      chownr: 1.1.4
+      mkdirp-classic: 0.5.3
+      pump: 3.0.4
+      tar-stream: 2.2.0
+
+  [email protected]:
+    dependencies:
+      bl: 4.1.0
+      end-of-stream: 1.4.5
+      fs-constants: 1.0.0
+      inherits: 2.0.4
+      readable-stream: 3.6.2
+
   [email protected]:
     dependencies:
       b4a: 1.8.1
@@ -11855,6 +12105,10 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]:
+    dependencies:
+      safe-buffer: 5.2.1
+
   [email protected]:
     dependencies:
       tagged-tag: 1.0.0
@@ -11998,7 +12252,7 @@ snapshots:
       escape-string-regexp: 5.0.0
       ufo: 1.6.4
 
-  [email protected]([email protected])([email protected]):
+  [email protected]([email protected]([email protected]))([email protected]):
     dependencies:
       anymatch: 3.1.3
       chokidar: 5.0.0
@@ -12009,7 +12263,7 @@ snapshots:
       ofetch: 1.5.1
       ufo: 1.6.4
     optionalDependencies:
-      db0: 0.3.4
+      db0: 0.3.4([email protected])
       ioredis: 5.11.1
 
   [email protected]:
@@ -12192,8 +12446,8 @@ snapshots:
       picomatch: 4.0.4
       std-env: 4.1.0
       tinybench: 2.9.0
-      tinyexec: 1.1.2
-      tinyglobby: 0.2.16
+      tinyexec: 1.2.4
+      tinyglobby: 0.2.17
       tinyrainbow: 3.1.0
       vite: 8.0.13(@types/[email protected])([email protected])([email protected])([email protected])([email protected])
       why-is-node-running: 2.3.0

+ 1 - 0
pnpm-workspace.yaml

@@ -3,5 +3,6 @@ packages:
   - "packages/*"
 allowBuilds:
   '@parcel/watcher': true
+  better-sqlite3: true
   esbuild: false
   vue-demi: false

+ 6 - 0
skills-lock.json

@@ -7,6 +7,12 @@
       "skillPath": "skills/nuxt-ui/SKILL.md",
       "computedHash": "2d58c6092b7028db223a819d5a37badc860a4036ca134a3e457ce7b056562f72"
     },
+    "tauri-v2": {
+      "source": "nodnarbnitram/claude-code-extensions",
+      "sourceType": "github",
+      "skillPath": ".claude/skills/tauri-v2/SKILL.md",
+      "computedHash": "377c61c46cf48c35b942042bc8abb9a039049a47f37d7f12b4f491d012777490"
+    },
     "vue": {
       "source": "antfu/skills",
       "sourceType": "github",

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio