diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 03a13e8..f74deca 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,8 +11,7 @@ updates: dependency-type: development ignore: # Majors are taken by hand: React, Vite, TypeScript, Tailwind, and KaTeX - # follow the pinned ZenNotes toolchain (the shell's copies are what - # app-core runs on via resolve.dedupe), and Capacitor needs a migration + # follow the pinned core package peer requirements, and Capacitor needs a migration # pass with a simulator run. KaTeX is 0.x, so its minors are majors. - dependency-name: "*" update-types: ["version-update:semver-major"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 569ec9a..dd403d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: jobs: verify: - name: TypeScript, source pin, and iOS build + name: TypeScript, package boundary, and iOS build runs-on: macos-latest steps: - name: Check out repository @@ -29,13 +29,12 @@ jobs: - name: Install mobile dependencies run: npm ci - - name: Prepare exact ZenNotes source - run: npm run source:prepare + - name: Verify installed core packages + run: npm run boundaries:check - name: Reject high-severity production advisories run: | npm audit --omit=dev --audit-level=high - npm --prefix .zennotes-source audit --omit=dev --audit-level=high - name: Test and typecheck bridges run: | diff --git a/.gitignore b/.gitignore index afe71d0..180d8e6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ build/ docs/releases/* + +/dist-boundary-check/ diff --git a/.zennotes-commit b/.zennotes-commit deleted file mode 100644 index 3119a4f..0000000 --- a/.zennotes-commit +++ /dev/null @@ -1 +0,0 @@ -104416f560bb9e709620cabe9e1f225306f7a4ea diff --git a/README.md b/README.md index 4220a80..14b3ce1 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,15 @@ the [zennotes monorepo](https://github.com/ZenNotes/zennotes)) inside a WKWebVie by a local-first vault on the device filesystem. Implements the architecture in `docs/specs/mobile/` (Phase 0 + the on-device parts of Phase 1). -The zennotes repo is consumed **read-only at the exact commit in -`.zennotes-commit`**. `npm run source:prepare` checks that commit out under the -ignored `.zennotes-source/` directory and installs its locked dependencies. -Every typecheck and release build verifies the pin; no ambient sibling checkout -can silently change a mobile binary. +The shell consumes immutable, compiled `@zennotes/app-core`, +`@zennotes/bridge-contract`, and `@zennotes/shared-domain` archives. The current +local candidates live in `vendor/zennotes`; its manifest records their source +identity and checksums. A clean checkout installs them with `npm ci`, without a +source clone or sibling repository. They have not been published. + +`npm run boundaries:check` verifies the pins, installed versions, singleton +React/CodeMirror peers, and public export usage. Native storage, iCloud, stable +vault identities, keyboard behavior, sync, and preferences remain in this repo. ## Architecture @@ -26,21 +30,21 @@ src/ events.ts VaultChangeEvent emitter (in-app writes + rescan) ui-mobile/ MobileShell.tsx bottom nav (capture ⊕ / search / sidebar / palette), - phone drawer behavior via the shared Zustand store + phone drawer behavior via public core snapshots/actions mobile.css safe areas, overlay drawers, keyboard handling ios/ Capacitor-generated Xcode project (appId md.zennotes) ``` -Key decisions (all forced by "don't modify the zennotes repo"): +Host decisions: -- **`runtime: 'web'`** — the bridge contract has no `'mobile'` runtime yet. +- **`hostKind: 'ios'`, `runtime: 'web'`** — the bridge contract has no `'mobile'` runtime yet. Every desktop-only affordance in app-core gates on `runtime === 'desktop'`, so `'web'` + the capability flags produces correct mobile behavior. When the contract gains `'mobile'` + the new capability flags (spec 02), flip it here. - **Vault location** — `Documents/ZenNotes/` in the app container (visible in the Files app via `UIFileSharingEnabled`). First run creates `My Vault` seeded with the official demo tour (imported read-only from - `apps/desktop/src/main/demo-tour-data.ts`). + `@zennotes/shared-domain/demo-tour-data`). - **On-disk contract is byte-compatible with desktop**: same folder layout (`inbox|quick|archive|trash`, `assets/`, legacy `attachements/` recognized — the misspelling is intentional and load-bearing), same `.zennotes/` @@ -48,7 +52,7 @@ Key decisions (all forced by "don't modify the zennotes repo"): rules, same NoteMeta extraction regexes. Includes desktop 2.20's `systemFolderPaths` remaps (vault.json can point `inbox` at `01 - Entry/` etc.) — classification, walking, capture targets, the drawer, and database - path composition all resolve through `@shared/system-folder-paths`, so a + path composition all resolve through `@zennotes/shared-domain/system-folder-paths`, so a remapped vault synced from a Mac files notes identically here. - **Desktop 2.20 features on mobile**: renaming a note carries its leading `# heading` along (runs in the shared store — nothing to port, verified on @@ -65,7 +69,7 @@ Key decisions (all forced by "don't modify the zennotes repo"): Settings → Editor → Text replacements), configurable tab size, manual kanban card order (`kanbanCardOrder` passes through the mobile vault.json layer verbatim). Remote reads use the shared absence-aware reader - (`@shared/remote-absence`): a 500 from a schema read surfaces as an error + (`@zennotes/shared-domain/remote-absence`): a 500 from a schema read surfaces as an error instead of adopting-and-overwriting the database sidecar; pre-2.20.2 servers that answer 500 for missing files are probed once per connection. - **TikZ** is capability-gated off (no WASM TeX on device); blocks show the @@ -77,8 +81,9 @@ Key decisions (all forced by "don't modify the zennotes repo"): ## Build & run ```sh -npm install -npm run sync # prepare pinned source + vite build + cap sync ios +npm ci +npm run boundaries:check +npm run sync # vite build + cap sync ios npx cap open ios # open in Xcode, or: xcodebuild -workspace ios/App/App.xcworkspace -scheme App \ -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build @@ -88,9 +93,10 @@ Dev loop against a browser (no simulator): `npm run dev` — note Capacitor plugins are absent in a plain browser, so vault I/O won't work; use the simulator for real testing. -To adopt a newer ZenNotes core, update `.zennotes-commit` to a reviewed full -commit SHA and run `npm run upstream`. Commit the pin with the mobile changes -that depend on it. +To adopt a newer core, copy the reviewed package archives and portable manifest +into `vendor/zennotes`, update the three exact dependencies, and refresh the lockfile. +Run the boundary check, tests, typecheck, and native build before changing the pin. +Retain the previous artifacts for rollback. Never resolve a mutable branch at build time. ## What works today (verified on the iPhone 17 Pro simulator) @@ -135,8 +141,7 @@ that depend on it. - The spec-06 **editing toolbar** docked above the soft keyboard (undo/redo, checkbox, bullet, heading cycle, bold/italic/highlight/code, link, wikilink, - tag, indent/outdent, dismiss) — drives the shared editor via the store's - `editorViewRef` + app-core's `lib/cm-format.ts`; auto-hides with a hardware + tag, indent/outdent, dismiss) — drives the shared editor through named public commands; auto-hides with a hardware keyboard - **Long-press context menus**: a 450ms press on chrome surfaces synthesizes the `contextmenu` event the desktop handlers already listen for (the @@ -213,8 +218,8 @@ kept off the object-storage request and a five-minute mobile transfer timeout. ## Release verification -Pull requests and `main` run bridge tests, a pinned-source typecheck, -production dependency audits for both repositories, a Capacitor sync, and an +Pull requests and `main` run bridge tests, an installed-package typecheck, +production dependency audits, a Capacitor sync, and an Xcode `build-for-testing` of the app and Cloud UI-test targets. Dependabot opens weekly npm and GitHub Actions updates. @@ -232,3 +237,5 @@ signed object upload, completion, manifest, and cleanup with a deterministic - Home-screen widget / App Shortcuts capture entry points - iPad split view (two notes side by side); Android (Phase 2) - Store distribution work (signing, TestFlight, App Store listing — spec 08) + +For device-level package checks, see [native boundary validation](docs/native-boundary-validation.md). diff --git a/docs/native-boundary-validation.md b/docs/native-boundary-validation.md new file mode 100644 index 0000000..a81ef7a --- /dev/null +++ b/docs/native-boundary-validation.md @@ -0,0 +1,60 @@ +# Native package boundary validation + +The host consumes the three archives pinned in `vendor/zennotes/manifest.json`. +There is no main-repository checkout or private editor/store import in the build. + +## Package checks + +```sh +npm ci +npm run boundaries:check +npm run typecheck +npm test +npm run build +``` + +Run the same checks in a fresh copy containing the package manifest/lock, vendor +archives, source, public assets, tooling, TypeScript/Vite/Tailwind/PostCSS config, +and native project. Omit `node_modules` and any historical source clone. + +## Disposable native runtime + +Use a newly created simulator/emulator with no real account or vault. The fixture +creates a uniquely named test vault and writes notes and attachments. Do not put +this fixture in a release or install it on a personal device. + +1. Run `npm run build:boundary-fixture`. Only this explicit command adds + `tooling/native-boundary-fixture.ts` to the app; ordinary `npm run build` does not. +2. In a disposable checkout, copy `dist-boundary-check/` into `dist/`, then run + `npx cap sync ios` and build the native debug/simulator app as below. +3. Install and launch on the disposable device. The fixture checks native typing, + exact Unicode and trailing whitespace, search, task observation, attachments, + note rename, comments, trash/restore, and whole-vault rename under the public + workspace transition lock. +4. Read `Documents/boundary-validation.json` in the app data container. It must + report `passed-awaiting-restart` with 20 checks and no error. +5. Terminate and relaunch the app without clearing its data. The report must now + be `restart-passed`, including vault identity, selected note and exact bytes. +6. Remove the disposable device when finished. Before a normal build, use + `npm run build` and `npx cap sync ios` to replace the fixture assets. + +The fixture uses public core APIs and the native filesystem bridge. It needs no +account credentials. It does not prove live Cloud sync, iCloud account behavior, +or every third-party storage provider; those remain separate release checks. + +## iOS native checks + +From the repository root, on a configured Xcode installation: + +```sh +xcodebuild build-for-testing -workspace ios/App/App.xcworkspace \ + -scheme AppCloudUITests -configuration Debug \ + -destination 'generic/platform=iOS Simulator' \ + -derivedDataPath /tmp/zn-boundary-ios-build CODE_SIGNING_ALLOWED=NO +``` + +Install the simulator app on a newly created simulator with `simctl install`. +Use `simctl get_app_container DEVICE md.zennotes data` to locate the report. +Do not run account-backed Cloud UI tests against a personal account as part of +this fixture. iCloud remains native-host-owned and requires a separate test +account/device validation; package adoption does not change its implementation. diff --git a/ios/App/Podfile b/ios/App/Podfile index 2a40c96..a04bbc3 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -30,4 +30,13 @@ end post_install do |installer| assertDeploymentTarget(installer) + # Match the app's existing minimum. Xcode 27 rejects older pod targets. + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + minimum = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] + if minimum && Gem::Version.new(minimum) < Gem::Version.new('15.0') + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0' + end + end + end end diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index a5c285f..f24f9f9 100644 --- a/ios/App/Podfile.lock +++ b/ios/App/Podfile.lock @@ -88,6 +88,6 @@ SPEC CHECKSUMS: IONFilesystemLib: 21a63377696b2d8fab5632ecfb7d2ac67bddb68a KeychainSwift: 4a71a45c802fd9e73906457c2dcbdbdc06c9419d -PODFILE CHECKSUM: 8c2072557d285523b52c09b408d696d31beaa1a6 +PODFILE CHECKSUM: 22927e348daa753f82fe2ad9a8f18100024b7007 COCOAPODS: 1.17.0 diff --git a/package-lock.json b/package-lock.json index 84d687b..753951d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,9 +28,13 @@ "@codemirror/search": "^6.5.8", "@codemirror/state": "^6.7.1", "@codemirror/view": "^6.43.9", + "@lezer/common": "^1.5.2", "@lezer/highlight": "^1.2.1", "@replit/codemirror-vim": "^6.4.0", "@xyflow/react": "^12.11.5", + "@zennotes/app-core": "file:vendor/zennotes/zennotes-app-core-2.50.4-core.h8a09555824b619b5.tgz", + "@zennotes/bridge-contract": "file:vendor/zennotes/zennotes-bridge-contract-2.50.4-boundaries.h776a0eb6d6ed1862.tgz", + "@zennotes/shared-domain": "file:vendor/zennotes/zennotes-shared-domain-2.50.4-boundaries.h776a0eb6d6ed1862.tgz", "codemirror": "^6.0.1", "dompurify": "^3.3.4", "function-plot": "^1.25.3", @@ -64,6 +68,7 @@ "@types/react-dom": "^18.3.5", "@vitejs/plugin-react": "^6.1.1", "autoprefixer": "^10.5.4", + "esbuild": "^0.28.2", "harper.js": "^2.7.0", "postcss": "^8.5.10", "tailwindcss": "^3.4.17", @@ -112,6 +117,15 @@ "node": ">=20.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@braintree/sanitize-url": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", @@ -675,463 +689,2207 @@ "w3c-keyname": "^2.2.4" } }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT" - }, - "node_modules/@iconify/utils": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz", - "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@antfu/install-pkg": "^1.1.0", - "@iconify/types": "^2.0.0", - "import-meta-resolve": "^4.2.0" + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@ionic/cli-framework-output": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", - "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@ionic/utils-terminal": "2.3.5", - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-array": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", - "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-fs": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", - "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/fs-extra": "^8.0.0", - "debug": "^4.0.0", - "fs-extra": "^9.0.0", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-fs/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/@ionic/utils-object": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", - "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-process": { - "version": "2.1.12", - "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.12.tgz", - "integrity": "sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@ionic/utils-object": "2.1.6", - "@ionic/utils-terminal": "2.3.5", - "debug": "^4.0.0", - "signal-exit": "^3.0.3", - "tree-kill": "^1.2.2", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.7.tgz", - "integrity": "sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-subprocess": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz", - "integrity": "sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==", + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@ionic/utils-array": "2.1.6", - "@ionic/utils-fs": "3.1.7", - "@ionic/utils-process": "2.1.12", - "@ionic/utils-stream": "3.1.7", - "@ionic/utils-terminal": "2.3.5", - "cross-spawn": "^7.0.3", - "debug": "^4.0.0", - "tslib": "^2.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@ionic/utils-terminal": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", - "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/slice-ansi": "^4.0.0", - "debug": "^4.0.0", - "signal-exit": "^3.0.3", - "slice-ansi": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "tslib": "^2.0.1", - "untildify": "^4.0.0", - "wrap-ansi": "^7.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=16.0.0" + "node": ">=18" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@excalidraw/excalidraw": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.18.1.tgz", + "integrity": "sha512-6i5Gt7IDTOH//qa0Z315Ly5iVRhjWpu2whrlQFqkuwrkKUWgRsMk0P5qdE7bpyDpai7jeLeWYkyj1eVAfni1lw==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "6.0.2", + "@excalidraw/laser-pointer": "1.3.1", + "@excalidraw/mermaid-to-excalidraw": "2.2.2", + "@excalidraw/random-username": "1.1.0", + "@radix-ui/react-popover": "1.1.6", + "@radix-ui/react-tabs": "1.0.2", + "browser-fs-access": "0.29.1", + "canvas-roundrect-polyfill": "0.0.1", + "clsx": "1.1.1", + "cross-env": "7.0.3", + "es6-promise-pool": "2.5.0", + "fractional-indexing": "3.2.0", + "fuzzy": "0.1.3", + "image-blob-reduce": "3.0.1", + "jotai": "2.11.0", + "jotai-scope": "0.7.2", + "lodash.debounce": "4.0.8", + "lodash.throttle": "4.1.1", + "nanoid": "3.3.3", + "open-color": "1.9.1", + "pako": "2.0.3", + "perfect-freehand": "1.2.0", + "pica": "7.1.1", + "png-chunk-text": "1.0.0", + "png-chunks-encode": "1.0.0", + "png-chunks-extract": "1.0.0", + "points-on-curve": "1.0.1", + "pwacompat": "2.0.17", + "roughjs": "4.6.4", + "sass": "1.51.0", + "tunnel-rat": "0.1.2" + }, + "peerDependencies": { + "react": "^17.0.2 || ^18.2.0 || ^19.0.0", + "react-dom": "^17.0.2 || ^18.2.0 || ^19.0.0" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/@braintree/sanitize-url": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.2.tgz", + "integrity": "sha512-Tbsj02wXCbqGmzdnXNk0SOF19ChhRU70BsroIi4Pm6Ehp56in6vch94mfbdQ17DozxkL3BAVjbZ4Qc1a0HFRAg==", + "license": "MIT" + }, + "node_modules/@excalidraw/excalidraw/node_modules/immutable": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.9.tgz", + "integrity": "sha512-ObHy4YN7ycwZOUCLI1/6svfyAFu7vL8RhAvVu/bh/RZW9EPlOyDaQ9jDQWCtdqzaXUjgXZCW1migtHE7YI7UGQ==", + "license": "MIT" + }, + "node_modules/@excalidraw/excalidraw/node_modules/points-on-curve": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-1.0.1.tgz", + "integrity": "sha512-3nmX4/LIiyuwGLwuUrfhTlDeQFlAhi7lyK/zcRNGhalwapDWgAGR82bUpmn2mA03vII3fvNCG8jAONzKXwpxAg==", + "license": "MIT" + }, + "node_modules/@excalidraw/excalidraw/node_modules/roughjs": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.4.tgz", + "integrity": "sha512-s6EZ0BntezkFYMf/9mGn7M8XGIoaav9QQBCnJROWB3brUWQ683Q2LbRD/hq0Z3bAJ/9NVpU/5LpiTWvQMyLDhw==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, + "node_modules/@excalidraw/excalidraw/node_modules/roughjs/node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/@excalidraw/excalidraw/node_modules/sass": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.51.0.tgz", + "integrity": "sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==", + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@excalidraw/laser-pointer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@excalidraw/laser-pointer/-/laser-pointer-1.3.1.tgz", + "integrity": "sha512-psA1z1N2qeAfsORdXc9JmD2y4CmDwmuMRxnNdJHZexIcPwaNEyIpNcelw+QkL9rz9tosaN9krXuKaRqYpRAR6g==", + "license": "MIT" + }, + "node_modules/@excalidraw/markdown-to-text": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@excalidraw/markdown-to-text/-/markdown-to-text-0.1.2.tgz", + "integrity": "sha512-1nDXBNAojfi3oSFwJswKREkFm5wrSjqay81QlyRv2pkITG/XYB5v+oChENVBQLcxQwX4IUATWvXM5BcaNhPiIg==", + "license": "MIT" + }, + "node_modules/@excalidraw/mermaid-to-excalidraw": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@excalidraw/mermaid-to-excalidraw/-/mermaid-to-excalidraw-2.2.2.tgz", + "integrity": "sha512-5VKQq5CdRocC82vOIUpQ5ufJOVV9FpBTdHGA+ULqazeIVV+cr299877omQCibsdS3Bpitz2fsnTwnIXEmLVDSg==", + "license": "MIT", + "dependencies": { + "@excalidraw/markdown-to-text": "0.1.2", + "@mermaid-js/parser": "^0.6.3", + "mermaid": "^11.12.1", + "nanoid": "4.0.2" + } + }, + "node_modules/@excalidraw/mermaid-to-excalidraw/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/@excalidraw/random-username": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@excalidraw/random-username/-/random-username-1.1.0.tgz", + "integrity": "sha512-nULYsQxkWHnbmHvcs+efMkJ4/9TtvNyFeLyHdeGxW0zHs6P+jYVqcRff9A6Vq9w9JXeDRnRh2VKvTtS19GW2qA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.3.tgz", + "integrity": "sha512-LPKOXPn/zV+zis1oOfGWogaXVpqUybF3ZS6SCZIsz8vg0ivVp9+fVqyYB7xq0aiST/VhUQYGO1qo6uoYSiEJqw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "import-meta-resolve": "^4.2.0" + } + }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", + "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.12.tgz", + "integrity": "sha512-Jqkgyq7zBs/v/J3YvKtQQiIcxfJyplPgECMWgdO0E1fKrrH8EF0QGHNJ9mJCn6PYe2UtHNS8JJf5G21e09DfYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.7.tgz", + "integrity": "sha512-eSELBE7NWNFIHTbTC2jiMvh1ABKGIpGdUIvARsNPMNQhxJB3wpwdiVnoBoTYp+5a6UUIww4Kpg7v6S7iTctH1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-3.0.1.tgz", + "integrity": "sha512-cT4te3AQQPeIM9WCwIg8ohroJ8TjsYaMb2G4ZEgv9YzeDqHZ4JpeIKqG2SoaA3GmVQ3sOfhPM6Ox9sxphV/d1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.12", + "@ionic/utils-stream": "3.1.7", + "@ionic/utils-terminal": "2.3.5", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", + "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/cpp": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.6.tgz", + "integrity": "sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/css": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.4.tgz", + "integrity": "sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/go": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz", + "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/java": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", + "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.4.tgz", + "integrity": "sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@lezer/php": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz", + "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.19.tgz", + "integrity": "sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/sass": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lezer/sass/-/sass-1.1.0.tgz", + "integrity": "sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/yaml": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz", + "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.4.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", + "integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==", + "license": "MIT" + }, + "node_modules/@mermaid-js/parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz", + "integrity": "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==", + "license": "MIT", + "dependencies": { + "@chevrotain/types": "~11.1.2" + } + }, + "node_modules/@myriaddreamin/typst-ts-renderer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@myriaddreamin/typst-ts-renderer/-/typst-ts-renderer-0.7.0.tgz", + "integrity": "sha512-3sXIGxZn9MufPrPn6251DeuLf2FIEILYNzY5lX0XOJmaIYqgvQ3qpfqkCjgQD+splBvt5R1N3BuuNFrZKsHhMw==", + "license": "Apache-2.0" + }, + "node_modules/@myriaddreamin/typst-ts-web-compiler": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@myriaddreamin/typst-ts-web-compiler/-/typst-ts-web-compiler-0.7.0.tgz", + "integrity": "sha512-nMwMcfOBy5pABPDuVM7/u8425SxhPUM+OJe6daqqgVOT3+neCTz4JKwx2L8XasfEHTNvd0cUI07LR9q5ADo7Gw==", + "license": "Apache-2.0" + }, + "node_modules/@myriaddreamin/typst.ts": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@myriaddreamin/typst.ts/-/typst.ts-0.7.0.tgz", + "integrity": "sha512-JtO5Td/1QesH1IBKAZtbayFNK8u2sl3iz18Y67uYqBEdcXiu3z+wpZcDDKlmVJvAcCgSjHTnuk6ZLfkYclrGGQ==", + "license": "Apache-2.0", + "dependencies": { + "idb": "^7.1.1" + }, + "peerDependencies": { + "@myriaddreamin/typst-ts-renderer": "^0.7.0", + "@myriaddreamin/typst-ts-web-compiler": "^0.7.0" + }, + "peerDependenciesMeta": { + "@myriaddreamin/typst-ts-renderer": { + "optional": true + }, + "@myriaddreamin/typst-ts-web-compiler": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.148.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", + "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.1.tgz", + "integrity": "sha512-uuiFbs+YCKjn3X1DTSx9G7BHApu4GHbi3kgiwsnFUbOKCrwejAJv4eE4Vc8C0Oaxt9T0aV4ox0WCOdx+39Xo+g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", + "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", + "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@lezer/common": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", - "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", - "license": "MIT" - }, - "node_modules/@lezer/cpp": { + "node_modules/@radix-ui/react-popover": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.6.tgz", - "integrity": "sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.6.tgz", + "integrity": "sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@lezer/css": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.4.tgz", - "integrity": "sha512-N+tn9tej2hPvyKgHEApMOQfHczDJCwxrRFS3SPn9QjYN+uwHvEDnCgKRrb3mxDYxRS8sKMM8fhC3+lc04Abz5Q==", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.3.0" + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@lezer/go": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz", - "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.3.0" + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@lezer/highlight": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", - "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.3.0" + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/@lezer/html": { - "version": "1.3.13", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", - "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.2.tgz", + "integrity": "sha512-HLK+CqD/8pN6GfJm3U+cqpqhSKYAWiOJDe+A+8MfxBnOue39QEeMa43csUn2CXCHQT0/mewh1LrrG4tfkM9DMA==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-collection": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/java": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", - "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10" } }, - "node_modules/@lezer/javascript": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", - "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/json": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", - "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/lr": { - "version": "1.4.10", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", - "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/markdown": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.4.tgz", - "integrity": "sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", + "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.5.0", - "@lezer/highlight": "^1.0.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/php": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz", - "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.1.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/python": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.19.tgz", - "integrity": "sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==", + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/rust": { + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", - "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.0.2.tgz", + "integrity": "sha512-gOUwh+HbjCuL0UCo8kZ+kdUEG8QtpdO4sMQduJ34ZEz0r4922g9REOBM+vIsfwtGxSug4Yb1msJMJYN2Bk8TpQ==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-direction": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.1", + "@radix-ui/react-roving-focus": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/sass": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/sass/-/sass-1.1.0.tgz", - "integrity": "sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==", + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", + "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", + "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", + "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.0.tgz", + "integrity": "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", + "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-use-layout-effect": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", + "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", + "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", + "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/xml": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", - "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz", + "integrity": "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@lezer/yaml": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz", - "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", + "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", "license": "MIT", "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.4.0" + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" } }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.3.tgz", - "integrity": "sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==", - "license": "MIT" + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@mermaid-js/parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.2.1.tgz", - "integrity": "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw==", + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", "license": "MIT", "dependencies": { - "@chevrotain/types": "~11.1.2" + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@radix-ui/react-use-callback-ref": "1.1.0" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", "license": "MIT", - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@radix-ui/rect": "1.1.0" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/@oxc-project/types": { - "version": "0.148.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", - "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", - "dev": true, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/oxc-project" + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" + }, "node_modules/@replit/codemirror-vim": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.4.0.tgz", @@ -1413,7 +3171,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@types/d3": { @@ -1908,6 +3666,97 @@ "d3-zoom": "^3.0.0" } }, + "node_modules/@zennotes/app-core": { + "version": "2.50.4-core.h8a09555824b619b5", + "resolved": "file:vendor/zennotes/zennotes-app-core-2.50.4-core.h8a09555824b619b5.tgz", + "integrity": "sha512-U1E7L1oiaNAqwMxER8ZsN/xiH3uS2bzvgg0eQDK6HZ+y+FQvqXzWs5mEsD63YYdumedlCO1lkbVezq7n8G3BGQ==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.18.3", + "@codemirror/commands": "^6.7.1", + "@codemirror/lang-cpp": "^6.0.3", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-go": "^6.0.1", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.1", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/lang-rust": "^6.0.2", + "@codemirror/lang-sql": "^6.10.0", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/lang-yaml": "^6.1.3", + "@codemirror/language-data": "^6.5.1", + "@codemirror/lint": "^6.9.7", + "@codemirror/search": "^6.5.8", + "@excalidraw/excalidraw": "^0.18.1", + "@myriaddreamin/typst-ts-renderer": "^0.7.0", + "@myriaddreamin/typst-ts-web-compiler": "^0.7.0", + "@myriaddreamin/typst.ts": "^0.7.0", + "@replit/codemirror-vim": "^6.3.0", + "@xyflow/react": "^12.11.2", + "@zennotes/bridge-contract": "2.50.4-boundaries.h776a0eb6d6ed1862", + "@zennotes/shared-domain": "2.50.4-boundaries.h776a0eb6d6ed1862", + "dompurify": "^3.3.4", + "function-plot": "^1.25.3", + "gray-matter": "^4.0.3", + "harper.js": "^2.7.0", + "hast": "^1.0.0", + "highlight.js": "^11.10.0", + "jsxgraph": "^1.12.2", + "katex": "^0.16.15", + "mdast": "^3.0.0", + "mermaid": "^11.4.1", + "prettier": "^3.8.2", + "rehype-highlight": "^7.0.1", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "rehype-stringify": "^10.0.1", + "remark-frontmatter": "^5.0.0", + "remark-gfm": "^4.0.0", + "remark-math": "^6.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.1", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", + "vscode-oniguruma": "^2.0.1", + "vscode-textmate": "^9.3.2" + }, + "peerDependencies": { + "@codemirror/language": "^6.10.6", + "@codemirror/state": "^6.5.0", + "@codemirror/view": "^6.35.3", + "@lezer/common": "^1.5.2", + "@lezer/highlight": "^1.2.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "vite": "^6.4.3 || ^7.0.0 || ^8.0.0", + "zustand": "^5.0.2" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/@zennotes/bridge-contract": { + "version": "2.50.4-boundaries.h776a0eb6d6ed1862", + "resolved": "file:vendor/zennotes/zennotes-bridge-contract-2.50.4-boundaries.h776a0eb6d6ed1862.tgz", + "integrity": "sha512-hHS62ENXjQ9ROVdyoC9CUmgXk0mzJU4UE5+8hwtATn+GreZBcKZZS1mdP0adWBjWwgGePfwqGovcojAutm+ntA==", + "license": "MIT" + }, + "node_modules/@zennotes/shared-domain": { + "version": "2.50.4-boundaries.h776a0eb6d6ed1862", + "resolved": "file:vendor/zennotes/zennotes-shared-domain-2.50.4-boundaries.h776a0eb6d6ed1862.tgz", + "integrity": "sha512-TV9u2Foo/NvetN+vBZ5BQlzCV+U0vok7Ojqkk9LLr0s+2y/1JN1qKVBfnPjqPttEXTXY2vgAL9fJa0vT28CynQ==", + "license": "MIT", + "dependencies": { + "@zennotes/bridge-contract": "2.50.4-boundaries.h776a0eb6d6ed1862", + "lz-string": "^1.5.0" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -1945,7 +3794,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -1971,6 +3819,18 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -2096,7 +3956,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2135,7 +3994,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -2144,6 +4002,12 @@ "node": ">=8" } }, + "node_modules/browser-fs-access": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.29.1.tgz", + "integrity": "sha512-LSvVX5e21LRrXqVMhqtAwj5xPgDb+fXAIH80NsnCQ9xuZPs2xWsOREi24RKgZa1XOiQRbcmVrv87+ulOKsgjxw==", + "license": "Apache-2.0" + }, "node_modules/browserslist": { "version": "4.28.8", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", @@ -2228,6 +4092,12 @@ ], "license": "CC-BY-4.0" }, + "node_modules/canvas-roundrect-polyfill": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/canvas-roundrect-polyfill/-/canvas-roundrect-polyfill-0.0.1.tgz", + "integrity": "sha512-yWq+R3U3jE+coOeEb3a3GgE2j/0MMiDKM/QpLb6h9ihf5fGY9UXtvK9o4vNqjWXoZz7/3EaSVU3IX53TvFFUOw==", + "license": "MIT" + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -2272,7 +4142,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -2297,7 +4166,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -2322,6 +4190,15 @@ "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", "license": "MIT" }, + "node_modules/clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/codemirror": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", @@ -2386,17 +4263,43 @@ "layout-base": "^1.0.0" } }, + "node_modules/crc-32": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-0.3.0.tgz", + "integrity": "sha512-kucVIjOmMc1f0tv53BJ/5WIX+MGLcKuoBhnGqQrgKJNqLByb/sVMWfW/Aw6hw0jgcqjJ2pi9E5y32zOIpaUlsA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/crelt": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", "license": "MIT" }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3003,12 +4906,18 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", @@ -3120,6 +5029,57 @@ "benchmarks" ] }, + "node_modules/es6-promise-pool": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/es6-promise-pool/-/es6-promise-pool-2.5.0.tgz", + "integrity": "sha512-VHErXfzR/6r/+yyzPKeBvO0lgjfC5cbDCQWjWwMZWSb6YU39TGIl51OUmCfWCq4ylMdJSB8zkz2vIuIeIxXApA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3258,14 +5218,12 @@ "version": "0.8.3", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", - "dev": true, "license": "MIT" }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3296,6 +5254,15 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/fractional-indexing": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fractional-indexing/-/fractional-indexing-3.2.0.tgz", + "integrity": "sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==", + "license": "CC0-1.0", + "engines": { + "node": "^14.13.1 || >=16.0.0" + } + }, "node_modules/fs-extra": { "version": "11.3.6", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz", @@ -3315,7 +5282,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3355,6 +5321,23 @@ "interval-arithmetic-eval": "^0.5.3" } }, + "node_modules/fuzzy": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/fuzzy/-/fuzzy-0.1.3.tgz", + "integrity": "sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -3386,6 +5369,12 @@ "node": ">=10.13.0" } }, + "node_modules/glur": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/glur/-/glur-1.1.2.tgz", + "integrity": "sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA==", + "license": "MIT" + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3418,7 +5407,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/harper.js/-/harper.js-2.7.0.tgz", "integrity": "sha512-INDnUMNJvQzv5Zv9lhgGuIRYNIpDvOXcieCbo5ED/dwn8V/02zVW5kKvU6jSJJHq1MpY0VDyg+5RjE7z5D+FeA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "fflate": "^0.8.2" @@ -3437,6 +5425,13 @@ "node": ">= 0.4" } }, + "node_modules/hast": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hast/-/hast-1.0.0.tgz", + "integrity": "sha512-vFUqlRV5C+xqP76Wwq2SrM0kipnmpxJm7OfvVXpB35Fp+Fn4MV+ozr+JZr5qFvyR1q/U+Foim2x+3P+x9S1PLA==", + "deprecated": "Renamed to rehype", + "license": "MIT" + }, "node_modules/hast-util-from-dom": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", @@ -3676,6 +5671,30 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "license": "ISC" + }, + "node_modules/image-blob-reduce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/image-blob-reduce/-/image-blob-reduce-3.0.1.tgz", + "integrity": "sha512-/VmmWgIryG/wcn4TVrV7cC4mlfUC/oyiKIfSg5eVM3Ten/c1c34RJhMYKCWTnoSMHSqXLt3tsrBR4Q2HInvN+Q==", + "license": "MIT", + "dependencies": { + "pica": "^7.1.0" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/import-meta-resolve": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", @@ -3690,7 +5709,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -3735,7 +5753,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -3789,7 +5806,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3809,7 +5825,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -3822,7 +5837,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -3857,7 +5871,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/jiti": { @@ -3870,6 +5883,37 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jotai": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.11.0.tgz", + "integrity": "sha512-zKfoBBD1uDw3rljwHkt0fWuja1B76R7CjznuBO+mSX6jpsO1EBeWNRKpeaQho9yPI/pvCv4recGfgOXGxwPZvQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/jotai-scope": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/jotai-scope/-/jotai-scope-0.7.2.tgz", + "integrity": "sha512-Gwed97f3dDObrO43++2lRcgOqw4O2sdr4JCjP/7eHK1oPACDJ7xKHGScpJX9XaflU+KBHXF+VhwECnzcaQiShg==", + "license": "MIT", + "peerDependencies": { + "jotai": ">=2.9.2", + "react": ">=17.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3973,7 +6017,7 @@ "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "dev": true, + "devOptional": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -4256,6 +6300,18 @@ "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4302,6 +6358,15 @@ "node": ">=12.0.0" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -4334,6 +6399,13 @@ "mr-parser": "^0.2.1" } }, + "node_modules/mdast": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast/-/mdast-3.0.0.tgz", + "integrity": "sha512-xySmf8g4fPKMeC07jXGz971EkLbWAJ83s4US2Tj9lEdnZ142UP5grN73H1Xd3HzrdbU5o9GYYP/y8F9ZSwLE9g==", + "deprecated": "`mdast` was renamed to `remark`", + "license": "MIT" + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -5298,6 +7370,16 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multimath": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/multimath/-/multimath-2.0.0.tgz", + "integrity": "sha512-toRx66cAMJ+Ccz7pMIg38xSIrtnbozk0dchXezwQDMgQmbGpfxjtv68H+L00iFL8hxDaVjrmwAFSb3I6bg8Q2g==", + "license": "MIT", + "dependencies": { + "glur": "^1.1.2", + "object-assign": "^4.1.1" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5314,7 +7396,6 @@ "version": "3.3.18", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, "funding": [ { "type": "github", @@ -5364,6 +7445,15 @@ "double-bits": "^1.1.0" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/node-releases": { "version": "2.0.53", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz", @@ -5378,7 +7468,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5388,7 +7477,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5422,6 +7510,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open-color": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/open-color/-/open-color-1.9.1.tgz", + "integrity": "sha512-vCseG/EQ6/RcvxhUcGJiHViOgrtz4x0XbZepXvKik66TMGkvbmjeJrKFyBEx6daG5rNyyd14zYXhz0hZVwQFOw==", + "license": "MIT" + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -5435,6 +7529,12 @@ "integrity": "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==", "license": "MIT" }, + "node_modules/pako": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.3.tgz", + "integrity": "sha512-WjR1hOeg+kki3ZIOjaf4b5WVcay1jaliKSYiEaB1XzwhMQZJxRdQRv0V31EKBYlxb4T7SK3hjfc/jxyU64BoSw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -5457,7 +7557,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5504,18 +7603,36 @@ "dev": true, "license": "MIT" }, + "node_modules/perfect-freehand": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.0.tgz", + "integrity": "sha512-h/0ikF1M3phW7CwpZ5MMvKnfpHficWoOEyr//KVNTxV4F6deRK1eYMtHyBKEAKFK0aXIEUK9oBvlF6PNXMDsAw==", + "license": "MIT" + }, + "node_modules/pica": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/pica/-/pica-7.1.1.tgz", + "integrity": "sha512-WY73tMvNzXWEld2LicT9Y260L43isrZ85tPuqRyvtkljSDLmnNFQmZICt4xUJMVulmcc6L9O7jbBrtx3DOz/YQ==", + "license": "MIT", + "dependencies": { + "glur": "^1.1.2", + "inherits": "^2.0.3", + "multimath": "^2.0.0", + "object-assign": "^4.1.1", + "webworkify": "^1.5.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -5559,6 +7676,31 @@ "node": ">=10.4.0" } }, + "node_modules/png-chunk-text": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-chunk-text/-/png-chunk-text-1.0.0.tgz", + "integrity": "sha512-DEROKU3SkkLGWNMzru3xPVgxyd48UGuMSZvioErCure6yhOc/pRH2ZV+SEn7nmaf7WNf3NdIpH+UTrRdKyq9Lw==", + "license": "MIT" + }, + "node_modules/png-chunks-encode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-chunks-encode/-/png-chunks-encode-1.0.0.tgz", + "integrity": "sha512-J1jcHgbQRsIIgx5wxW9UmCymV3wwn4qCCJl6KYgEU/yHCh/L2Mwq/nMOkRPtmV79TLxRZj5w3tH69pvygFkDqA==", + "license": "MIT", + "dependencies": { + "crc-32": "^0.3.0", + "sliced": "^1.0.1" + } + }, + "node_modules/png-chunks-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-chunks-extract/-/png-chunks-extract-1.0.0.tgz", + "integrity": "sha512-ZiVwF5EJ0DNZyzAqld8BP1qyJBaGOFaq9zl579qfbkcmOwWLLO4I9L8i2O4j3HkI6/35i0nKG2n+dZplxiT89Q==", + "license": "MIT", + "dependencies": { + "crc-32": "^0.3.0" + } + }, "node_modules/points-on-curve": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", @@ -5579,7 +7721,7 @@ "version": "8.5.26", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", - "dev": true, + "devOptional": true, "funding": [ { "type": "opencollective", @@ -5738,6 +7880,21 @@ "dev": true, "license": "MIT" }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -5772,6 +7929,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/pwacompat": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/pwacompat/-/pwacompat-2.0.17.tgz", + "integrity": "sha512-6Du7IZdIy7cHiv7AhtDy4X2QRM8IAD5DII69mt5qWibC2d15ZU8DmBG1WdZKekG11cChSu4zkSUGPF9sweOl6w==", + "license": "Apache-2.0" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5818,6 +7981,75 @@ "react": "^18.3.1" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -5847,7 +8079,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -6098,7 +8329,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.148.0", @@ -6197,6 +8428,63 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/sass": { + "version": "1.104.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.104.1.tgz", + "integrity": "sha512-yDA+1aIG3EHgN4V/BvuhCvu61FF6hEd4e+9DxikUm9U0CAGuvdIZ/UYy7qbOxjhbbWQraoyLlMuzdRGOSV5Bmw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/sax": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", @@ -6243,7 +8531,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -6256,7 +8543,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6294,11 +8580,17 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==", + "deprecated": "Unsupported", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6566,7 +8858,7 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -6583,7 +8875,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -6601,7 +8893,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12" @@ -6614,7 +8906,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -6675,6 +8966,43 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-rat": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tunnel-rat/-/tunnel-rat-0.1.2.tgz", + "integrity": "sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==", + "license": "MIT", + "dependencies": { + "zustand": "^4.3.2" + } + }, + "node_modules/tunnel-rat/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -6862,6 +9190,49 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", @@ -6937,7 +9308,7 @@ "version": "8.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", @@ -7015,7 +9386,7 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12" @@ -7052,11 +9423,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/webworkify": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/webworkify/-/webworkify-1.5.0.tgz", + "integrity": "sha512-AMcUeyXAhbACL8S2hqqdqOLqvJ8ylmIbNwUIqQujRSouf4+eUFaXbG6F1Rbu+srlJMmxQWsiU7mOJi0nMBfM1g==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" diff --git a/package.json b/package.json index 78d2bd5..a2154d1 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,15 @@ "homepage": "https://zennotes.org", "scripts": { "dev": "vite", - "source:prepare": "sh tooling/prepare-zennotes.sh", - "prebuild": "npm run source:prepare", "build": "vite build", "test": "node --test", "e2e:cloud": "node tooling/cloud-direct-upload-e2e.mjs", - "pretypecheck": "npm run source:prepare", "typecheck": "tsc --noEmit", "sync": "npm run build && cap sync ios", - "upstream": "sh tooling/upstream-check.sh", - "ios": "npm run build && cap sync ios && cap open ios" + "upstream": "npm run boundaries:check && npm run typecheck", + "ios": "npm run build && cap sync ios && cap open ios", + "boundaries:check": "node tooling/check-core-boundary.mjs", + "build:boundary-fixture": "node tooling/build-native-boundary-fixture.mjs" }, "dependencies": { "@aparajita/capacitor-secure-storage": "7.1.6", @@ -66,7 +65,11 @@ "unist-util-visit": "^5.0.0", "vscode-oniguruma": "^2.0.1", "vscode-textmate": "^9.3.2", - "zustand": "^5.0.15" + "zustand": "^5.0.15", + "@zennotes/app-core": "file:vendor/zennotes/zennotes-app-core-2.50.4-core.h8a09555824b619b5.tgz", + "@zennotes/shared-domain": "file:vendor/zennotes/zennotes-shared-domain-2.50.4-boundaries.h776a0eb6d6ed1862.tgz", + "@zennotes/bridge-contract": "file:vendor/zennotes/zennotes-bridge-contract-2.50.4-boundaries.h776a0eb6d6ed1862.tgz", + "@lezer/common": "^1.5.2" }, "devDependencies": { "@capacitor/cli": "^7.6.8", @@ -79,6 +82,16 @@ "postcss": "^8.5.10", "tailwindcss": "^3.4.17", "typescript": "^5.7.2", - "vite": "^8.2.2" + "vite": "^8.2.2", + "esbuild": "^0.28.2" + }, + "overrides": { + "@excalidraw/excalidraw": { + "nanoid": "3.3.18" + }, + "@excalidraw/mermaid-to-excalidraw": { + "@mermaid-js/parser": "1.2.1", + "nanoid": "5.1.16" + } } } diff --git a/src/bridge/events.ts b/src/bridge/events.ts index 2c23ecb..859336a 100644 --- a/src/bridge/events.ts +++ b/src/bridge/events.ts @@ -5,7 +5,7 @@ * runs after every sync. The subscription API is identical to desktop/web so * app-core consumes it as-is. */ -import type { VaultChangeEvent } from '@bridge-contract/ipc' +import type { VaultChangeEvent } from '@zennotes/bridge-contract/ipc' type VaultChangeListener = (ev: VaultChangeEvent) => void diff --git a/src/bridge/imported-assets.ts b/src/bridge/imported-assets.ts index 1195a1f..6c0cd60 100644 --- a/src/bridge/imported-assets.ts +++ b/src/bridge/imported-assets.ts @@ -1,13 +1,13 @@ /** * Where an imported file goes and how it is linked. * - * A leaf module on purpose: `vault-core` reaches `@shared/*` through a Vite + * A leaf module on purpose: `vault-core` reaches `@zennotes/shared-domain/*` through a Vite * alias, which plain `node --test` cannot resolve, so nothing there is * unit-testable. These rules are where the attachment bugs hid, so they live * where a test can reach them. The vault core re-exports them so existing * importers remain unaffected. */ -import type { ImportedAssetKind } from '@bridge-contract/ipc' +import type { ImportedAssetKind } from '@zennotes/bridge-contract/ipc' export const ASSETS_DIR = 'assets' diff --git a/src/bridge/link-metadata.ts b/src/bridge/link-metadata.ts index 57b7fbb..a90425d 100644 --- a/src/bridge/link-metadata.ts +++ b/src/bridge/link-metadata.ts @@ -14,7 +14,7 @@ * (metadata lives in ``, so the whole page is never needed). */ import { CapacitorHttp } from '@capacitor/core' -import type { LinkMetadata } from '@shared/ipc' +import type { LinkMetadata } from '@zennotes/shared-domain/ipc' const TIMEOUT_MS = 6000 const MAX_CHARS = 512 * 1024 diff --git a/src/bridge/mobile-bridge.ts b/src/bridge/mobile-bridge.ts index 5368e4a..0e0e017 100644 --- a/src/bridge/mobile-bridge.ts +++ b/src/bridge/mobile-bridge.ts @@ -1,3 +1,4 @@ +import { relocateLocalVault } from '@zennotes/app-core/workspace' /** * The mobile `window.zen` — third ZenBridge implementation (after Electron IPC * and the web HTTP bridge). Vault operations run against the on-device vault @@ -36,13 +37,13 @@ import type { VaultTextSearchBackendPreference, VaultTextSearchCapabilities, VaultTextSearchMatch -} from '@shared/ipc' -import { createDatabaseOps } from '@shared/database-ops' +} from '@zennotes/shared-domain/ipc' +import { createDatabaseOps } from '@zennotes/shared-domain/database-ops' import type { CustomCodeLanguage, CustomCodeLanguageInstallInput, CustomCodeLanguageUpdateInput -} from '@shared/custom-code-languages' +} from '@zennotes/shared-domain/custom-code-languages' import type { ApplyWorkflowInput, WorkflowFile, @@ -50,12 +51,12 @@ import type { WorkflowRunSummary, WorkflowUndoResult, WriteWorkflowInput -} from '@bridge-contract/workflows' +} from '@zennotes/bridge-contract/workflows' import type { McpClientStatus, McpInstructionsPayload, McpServerRuntime -} from '@shared/mcp-clients' +} from '@zennotes/shared-domain/mcp-clients' import { MobileVault } from './vault-fs' import { listVaultDirs, VAULTS_DIR } from './native-fs' import { randomUUID } from './uuid' @@ -263,23 +264,22 @@ export async function renameVault(entry: MobileVaultEntry, newName: string): Pro if (!clean) throw new Error('Enter a name.') if (clean === entry.name) return if (entry.tier === 'external') throw new Error('Rename this folder in the Files app.') - await assertNameFree(entry.tier, clean) const wasCurrent = isCurrentVaultEntry(entry) - if (entry.tier === 'icloud') { - await Filesystem.rename({ - from: await icloudVaultUrl(entry.name), - to: await icloudVaultUrl(clean) - }) - if (wasCurrent) await openVaultByName(clean, await icloudVaultUrl(clean)) - } else { - await Filesystem.rename({ - from: `${VAULTS_DIR}/${entry.name}`, - to: `${VAULTS_DIR}/${clean}`, - directory: Directory.Documents, - toDirectory: Directory.Documents - }) - if (wasCurrent) await openVaultByName(clean) - } + const token = (name: string): string => entry.tier === 'icloud' + ? `${ICLOUD_VAULT_ROOT_PREFIX}${encodeURIComponent(name)}` : `${VAULT_ROOT_PREFIX}${name}` + let from = '', to = '' + await relocateLocalVault({ + ...(wasCurrent ? { reopen: { source: token(entry.name), destination: token(clean) } } : {}), + move: async () => { + await assertNameFree(entry.tier, clean) + from = entry.tier === 'icloud' ? await icloudVaultUrl(entry.name) : `${VAULTS_DIR}/${entry.name}` + to = entry.tier === 'icloud' ? await icloudVaultUrl(clean) : `${VAULTS_DIR}/${clean}` + await Filesystem.rename({ from, to, ...(entry.tier === 'icloud' ? {} : { directory: Directory.Documents, toDirectory: Directory.Documents }) }) + }, + rollback: async () => { + await Filesystem.rename({ from: to, to: from, ...(entry.tier === 'icloud' ? {} : { directory: Directory.Documents, toDirectory: Directory.Documents }) }) + } + }) } /** Permanently removes the vault directory and everything in it. The UI owns @@ -310,23 +310,26 @@ export function forgetExternalVault(): void { * the hood, so notes transfer — not copy). Reopens it when it's current. */ export async function moveVault(entry: MobileVaultEntry, to: 'local' | 'icloud'): Promise { if (entry.tier === 'external' || entry.tier === to) return - await assertNameFree(to, entry.name) const wasCurrent = isCurrentVaultEntry(entry) - const localPath = await localVaultPath(entry.name) - if (to === 'icloud') { - const status = await icloudStatus() - if (!status.available) { - throw new Error('iCloud is not available. Sign in to iCloud and turn on iCloud Drive.') + const token = (tier: 'local' | 'icloud'): string => tier === 'icloud' + ? `${ICLOUD_VAULT_ROOT_PREFIX}${encodeURIComponent(entry.name)}` : `${VAULT_ROOT_PREFIX}${entry.name}` + let localPath = '' + await relocateLocalVault({ + ...(wasCurrent ? { reopen: { source: token(entry.tier), destination: token(to) } } : {}), + move: async () => { + await assertNameFree(to, entry.name) + localPath = await localVaultPath(entry.name) + if (to === 'icloud') { + const status = await icloudStatus() + if (!status.available) throw new Error('iCloud is not available. Sign in to iCloud and turn on iCloud Drive.') + await ICloudVault.enable({ localPath, name: entry.name }) + } else await ICloudVault.disable({ name: entry.name, localPath }) + }, + rollback: async () => { + if (to === 'icloud') await ICloudVault.disable({ name: entry.name, localPath }) + else await ICloudVault.enable({ localPath, name: entry.name }) } - await ICloudVault.enable({ localPath, name: entry.name }) - } else { - await ICloudVault.disable({ name: entry.name, localPath }) - } - if (wasCurrent) { - setStoragePref(to) - if (to === 'icloud') await openVaultByName(entry.name, await icloudVaultUrl(entry.name)) - else await openVaultByName(entry.name) - } + }) } const MOBILE_CAPABILITIES: ZenCapabilities = { @@ -360,7 +363,8 @@ function mobileAppInfo(): ZenAppInfo { version: appVersion, description: 'ZenNotes for iPhone', homepage: 'https://zennotes.org', - runtime: 'web' + runtime: 'web', + hostKind: 'ios' } } @@ -581,7 +585,7 @@ export async function importPendingShares(): Promise { } // -------------------------------------------------------------------- -// Databases — the shared composition (@shared/database-ops, extracted from +// Databases — the shared composition (@zennotes/shared-domain/database-ops, extracted from // the web bridge in 2.20) bound to the active vault's file ops. Remap-aware: // the shared layout honors vault.json `systemFolderPaths` when composing // `.base/` paths, which the old local copy of this glue did not. @@ -800,8 +804,8 @@ export const mobileBridge: ZenBridge = { getCapabilities: (): ZenCapabilities => MOBILE_CAPABILITIES, getAppInfo: (): ZenAppInfo => mobileAppInfo(), - platform: async () => 'darwin' as NodeJS.Platform, - platformSync: () => 'darwin' as NodeJS.Platform, + platform: async () => 'darwin' as const, + platformSync: () => 'darwin' as const, listSystemFonts: async () => [ 'Avenir', 'Charter', @@ -1098,6 +1102,7 @@ export const mobileBridge: ZenBridge = { // External file links name OS paths outside the iOS sandbox; the exact // 'desktop-only' token makes app-core show its friendly toast. openExternalFile: async () => ({ ok: false, error: 'desktop-only' }), + openExternalUrl: async () => ({ ok: false, error: 'desktop-only' }), // A vault attachment, unlike an arbitrary OS path, is a file this app owns // — so the phone can genuinely open it (share sheet / Quick Look) instead // of answering 'desktop-only' the way the web bridge must. diff --git a/src/bridge/native-fs.ts b/src/bridge/native-fs.ts index 617d16a..199182f 100644 --- a/src/bridge/native-fs.ts +++ b/src/bridge/native-fs.ts @@ -141,7 +141,8 @@ export class NativeFs { async readTextOrNull(relPath: string): Promise { try { return await this.readText(relPath) - } catch { + } catch (error) { + if (!isNotFoundError(error)) throw error return null } } diff --git a/src/bridge/remote-client.ts b/src/bridge/remote-client.ts index 7a8f80e..de7b89c 100644 --- a/src/bridge/remote-client.ts +++ b/src/bridge/remote-client.ts @@ -25,8 +25,8 @@ import type { VaultInfo, VaultSettings, VaultTextSearchMatch -} from '@shared/ipc' -import type { VaultTask } from '@shared/tasks' +} from '@zennotes/shared-domain/ipc' +import type { VaultTask } from '@zennotes/shared-domain/tasks' import { importedAssetFilename } from './imported-assets.ts' export interface RemoteClientOptions { diff --git a/src/bridge/remote-vault.ts b/src/bridge/remote-vault.ts index 80e56bf..9a9af24 100644 --- a/src/bridge/remote-vault.ts +++ b/src/bridge/remote-vault.ts @@ -29,12 +29,12 @@ import type { VaultInfo, VaultSettings, VaultTextSearchMatch -} from '@shared/ipc' -import type { VaultTask } from '@shared/tasks' -import type { CustomTemplateFile, WriteTemplateInput } from '@bridge-contract/templates' -import type { ImportedAsset } from '@shared/ipc' -import { createAbsenceAwareReader } from '@shared/remote-absence' -import { pastedImageFilename } from '@shared/pasted-image' +} from '@zennotes/shared-domain/ipc' +import type { VaultTask } from '@zennotes/shared-domain/tasks' +import type { CustomTemplateFile, WriteTemplateInput } from '@zennotes/bridge-contract/templates' +import type { ImportedAsset } from '@zennotes/shared-domain/ipc' +import { createAbsenceAwareReader } from '@zennotes/shared-domain/remote-absence' +import { pastedImageFilename } from '@zennotes/shared-domain/pasted-image' import { emitVaultChange } from './events' import { importedAssetFilename } from './imported-assets' import { RemoteClient, RemoteRequestError } from './remote-client' diff --git a/src/bridge/remote-workspace.ts b/src/bridge/remote-workspace.ts index 0e6c222..9da2f52 100644 --- a/src/bridge/remote-workspace.ts +++ b/src/bridge/remote-workspace.ts @@ -15,7 +15,7 @@ import type { RemoteWorkspaceProfileInput, ServerCapabilities, VaultInfo -} from '@shared/ipc' +} from '@zennotes/shared-domain/ipc' import { RemoteClient, normalizeBaseUrl } from './remote-client' import { randomUUID } from './uuid' import { RemoteVault } from './remote-vault' diff --git a/src/bridge/tikz.ts b/src/bridge/tikz.ts index 57fe7e6..a6131fe 100644 --- a/src/bridge/tikz.ts +++ b/src/bridge/tikz.ts @@ -16,7 +16,7 @@ * per-source caching, and a serialized render queue (the TeX engine does not * tolerate concurrent runs). */ -import type { TikzRenderResponse } from '@shared/ipc' +import type { TikzRenderResponse } from '@zennotes/shared-domain/ipc' const ASSET_BASE = 'tikzjax/' // Generous: the first render on a cold device includes engine load + wasm diff --git a/src/bridge/vault-core.ts b/src/bridge/vault-core.ts index aad3b3c..9e736ed 100644 --- a/src/bridge/vault-core.ts +++ b/src/bridge/vault-core.ts @@ -6,12 +6,12 @@ * The `attachements` (sic) constant is the intentional, load-bearing legacy * spelling — do not "fix" it and do not add an `attachments` variant. */ -import type { ImportedAssetKind, NoteFolder, VaultSettings } from '@bridge-contract/ipc' +import type { ImportedAssetKind, NoteFolder, VaultSettings } from '@zennotes/bridge-contract/ipc' import { resolveFolderPath, systemFolderForDirName, type SystemFolderPaths -} from '@shared/system-folder-paths' +} from '@zennotes/shared-domain/system-folder-paths' import { ASSETS_DIR } from './imported-assets' // Re-exported so every existing importer keeps reaching them here; they live in diff --git a/src/bridge/vault-fs.ts b/src/bridge/vault-fs.ts index 229d9f0..05456a0 100644 --- a/src/bridge/vault-fs.ts +++ b/src/bridge/vault-fs.ts @@ -1,3 +1,4 @@ +import { relocateVaultEntries, type VaultRelocation, type VaultRelocationIO } from '@zennotes/shared-domain/vault-relocation' /** * The on-device vault: desktop `vault.ts` semantics reimplemented over the * Capacitor Filesystem (there is no Node `fs` in a WKWebView). Same on-disk @@ -19,28 +20,28 @@ import type { VaultDemoTourResult, VaultSettings, VaultTextSearchMatch -} from '@bridge-contract/ipc' -import { DEFAULT_VAULT_SETTINGS } from '@bridge-contract/ipc' -import type { CustomTemplateFile, WriteTemplateInput } from '@bridge-contract/templates' -import type { VaultTask } from '@shared/tasks' -import { parseTaskFile, parseTasksFromBody } from '@shared/tasks' -import { normalizeHarperVaultState } from '@shared/harper-settings' -import { normalizeNoteComments } from '@shared/note-comments' +} from '@zennotes/bridge-contract/ipc' +import { DEFAULT_VAULT_SETTINGS } from '@zennotes/bridge-contract/ipc' +import type { CustomTemplateFile, WriteTemplateInput } from '@zennotes/bridge-contract/templates' +import type { VaultTask } from '@zennotes/shared-domain/tasks' +import { parseTaskFile, parseTasksFromBody } from '@zennotes/shared-domain/tasks' +import { normalizeHarperVaultState } from '@zennotes/shared-domain/harper-settings' +import { normalizeNoteComments } from '@zennotes/shared-domain/note-comments' import { isPathExcludedFromTasks, normalizeTasksExcludedFolders -} from '@shared/tasks-excluded-folders' -import { pastedImageFilename } from '@shared/pasted-image' +} from '@zennotes/shared-domain/tasks-excluded-folders' +import { pastedImageFilename } from '@zennotes/shared-domain/pasted-image' import { randomUUID } from './uuid' import { bytesToBase64 } from './base64' -import { isFormDirName, isDatabaseInternalPath } from '@shared/databases' -import { emptyExcalidrawDocument } from '@shared/excalidraw' -import { DEMO_TOUR_ASSETS, DEMO_TOUR_NOTES } from '@desktop-main/demo-tour-data' +import { isFormDirName, isDatabaseInternalPath } from '@zennotes/shared-domain/databases' +import { emptyExcalidrawDocument } from '@zennotes/shared-domain/excalidraw' +import { DEMO_TOUR_ASSETS, DEMO_TOUR_NOTES } from '@zennotes/shared-domain/demo-tour-data' import { WELCOME_NOTE_PATH, WELCOME_NOTE_BODY } from './welcome-note' import { rewriteWikilinksForRename, type RenameNoteRef -} from '@desktop-main/wikilink-rename' +} from '@zennotes/shared-domain/wikilink-rename' import { NativeFs } from './native-fs' import { ensureDownloaded } from './icloud' import { emitVaultChange } from './events' @@ -83,7 +84,7 @@ import { normalizeSystemFolderPaths, resolveFolderPath, type SystemFolderPaths -} from '@shared/system-folder-paths' +} from '@zennotes/shared-domain/system-folder-paths' const META_CACHE_FILE = `${INTERNAL_VAULT_DIR}/mobile-note-meta-cache-v1.json` /** Restore metadata written next to each deleted asset (desktop parity). */ @@ -679,6 +680,37 @@ export class MobileVault { return await this.metaForPath(rel) } + private relocationIO(): VaultRelocationIO { + return { + stat: path => this.fs.statVerified(path), + mkdir: path => this.fs.mkdir(path), + rename: (from, to) => this.fs.rename(from, to) + } + } + + private async relocateNote(oldPath: string, newPath: string): Promise { + await relocateVaultEntries(this.relocationIO(), [ + { from: oldPath, to: newPath, required: true }, + { from: this.commentsPathFor(oldPath), to: this.commentsPathFor(newPath) } + ]) + this.invalidateMeta(oldPath) + } + + /** Detach both trees before cleanup so a failed move can restore the original. */ + private async detachContent(path: string, comments: string, required = true): Promise { + const temporary = `${INTERNAL_VAULT_DIR}/delete-${uuid()}` + const moves: VaultRelocation[] = [ + { from: path, to: `${temporary}/content`, required }, + { from: comments, to: `${temporary}/comments` } + ] + await relocateVaultEntries(this.relocationIO(), moves) + try { + if (await this.fs.statVerified(temporary) !== null) await this.fs.rmdir(temporary) + } catch (error) { + console.warn('Detached deleted content retained for cleanup', temporary, error) + } + } + async renameNote(relPath: string, nextTitle: string): Promise { const rel = resolveSafeRel(relPath) const folder = await this.folderOf(rel) @@ -693,9 +725,7 @@ export class MobileVault { } // Snapshot for inbound wikilink rewriting before the rename lands. const preNotes = await this.listNotes() - await this.fs.rename(rel, target) - this.invalidateMeta(rel) - await this.moveNoteComments(rel, target) + await this.relocateNote(rel, target) emitVaultChange({ kind: 'unlink', path: rel, folder, scope: 'content' }) emitVaultChange({ kind: 'add', path: target, folder, scope: 'content' }) await this.updateInboundWikilinks(preNotes, rel, trimmed) @@ -760,9 +790,7 @@ export class MobileVault { const baseTitle = stemName(filename) const finalTitle = await this.uniqueTitle(destDir, baseTitle, ext) const destRel = joinPath(destDir, `${finalTitle}${ext}`) - await this.fs.rename(rel, destRel) - this.invalidateMeta(rel) - await this.moveNoteComments(rel, destRel) + await this.relocateNote(rel, destRel) emitVaultChange({ kind: 'unlink', path: rel, folder: sourceFolder, scope: 'content' }) emitVaultChange({ kind: 'add', path: destRel, folder: target, scope: 'content' }) return await this.metaForPath(destRel) @@ -783,25 +811,17 @@ export class MobileVault { async emptyTrash(): Promise { const trashDir = await this.folderRootRel('trash') - const entries = await this.fs.readdir(trashDir) - for (const entry of entries) { - const rel = `${trashDir}/${entry.name}` - await this.removeNoteComments(rel) - if (entry.type === 'directory') { - await this.fs.rmdir(rel).catch(() => {}) - } else { - await this.fs.deleteFile(rel).catch(() => {}) - } - this.invalidateMeta(rel) - emitVaultChange({ kind: 'unlink', path: rel, folder: 'trash', scope: 'content' }) + await this.detachContent(trashDir, `${INTERNAL_VAULT_DIR}/${NOTE_COMMENTS_DIR}/${trashDir}`, false) + for (const key of [...this.metaCache.keys()]) { + if (key.startsWith(`${trashDir}/`)) this.invalidateMeta(key) } + emitVaultChange({ kind: 'unlink', path: trashDir, folder: 'trash', scope: 'folder' }) } async deleteNote(relPath: string): Promise { const rel = resolveSafeRel(relPath) const folder = (await this.folderOf(rel)) ?? 'trash' - await this.fs.deleteFile(rel) - await this.removeNoteComments(rel) + await this.detachContent(rel, this.commentsPathFor(rel)) this.invalidateMeta(rel) emitVaultChange({ kind: 'unlink', path: rel, folder, scope: 'content' }) } @@ -842,9 +862,7 @@ export class MobileVault { `${await this.uniqueTitle(destDir, baseTitle, ext || '.md')}${ext || '.md'}` ) if (destRel === rel) return await this.metaForPath(rel) - await this.fs.rename(rel, destRel) - this.invalidateMeta(rel) - await this.moveNoteComments(rel, destRel) + await this.relocateNote(rel, destRel) emitVaultChange({ kind: 'unlink', path: rel, folder: sourceFolder, scope: 'content' }) emitVaultChange({ kind: 'add', path: destRel, folder: targetFolder, scope: 'content' }) return await this.metaForPath(destRel) @@ -878,7 +896,6 @@ export class MobileVault { } const parent = dirName(newRel) if (parent) await this.fs.mkdir(parent) - await this.fs.rename(oldRel, newRel) // Re-key folder icons/colors + drop stale meta cache entries under the old path. const settings = await this.getVaultSettings() const rekey = (map: Record): Record => { @@ -892,10 +909,30 @@ export class MobileVault { } return out } - await this.setVaultSettings({ + const settingsPath = `${INTERNAL_VAULT_DIR}/vault.json` + const hadSettings = await this.fs.statVerified(settingsPath) !== null + const originalSettings = hadSettings ? await this.fs.readText(settingsPath) : null + await relocateVaultEntries(this.relocationIO(), [ + { from: oldRel, to: newRel, required: true }, + { from: `${INTERNAL_VAULT_DIR}/${NOTE_COMMENTS_DIR}/${oldRel}`, + to: `${INTERNAL_VAULT_DIR}/${NOTE_COMMENTS_DIR}/${newRel}` } + ], async () => { + try { + await this.setVaultSettings({ ...settings, folderIcons: rekey(settings.folderIcons as Record) as VaultSettings['folderIcons'], folderColors: rekey(settings.folderColors as Record) as VaultSettings['folderColors'] + }) + } catch (error) { + try { + if (originalSettings !== null) await this.fs.writeText(settingsPath, originalSettings) + else if (await this.fs.statVerified(settingsPath) !== null) await this.fs.deleteFile(settingsPath) + this.settingsCache = settings + } catch (rollback) { + throw new AggregateError([error, rollback], 'FOLDER_STATE_UNCERTAIN: Could not restore vault settings') + } + throw error + } }) for (const key of [...this.metaCache.keys()]) { if (key.startsWith(`${oldRel}/`)) this.invalidateMeta(key) @@ -910,7 +947,7 @@ export class MobileVault { const clean = subpath.replace(/^\/+|\/+$/g, '') if (!clean) return const rel = resolveSafeRel(joinPath(topRel, clean)) - await this.fs.rmdir(rel) + await this.detachContent(rel, `${INTERNAL_VAULT_DIR}/${NOTE_COMMENTS_DIR}/${rel}`) for (const key of [...this.metaCache.keys()]) { if (key.startsWith(`${rel}/`)) this.invalidateMeta(key) } @@ -981,24 +1018,6 @@ export class MobileVault { return comments } - private async moveNoteComments(oldRel: string, newRel: string): Promise { - const oldPath = this.commentsPathFor(oldRel) - const raw = await this.fs.readTextOrNull(oldPath) - if (raw === null) return - try { - const parsed = JSON.parse(raw) as { comments?: NoteComment[] } - const comments = (parsed.comments ?? []).map((c) => ({ ...c, notePath: newRel })) - await this.writeCommentsFile(newRel, comments) - } catch { - // unreadable sidecar — drop it - } - await this.fs.deleteFile(oldPath).catch(() => {}) - } - - private async removeNoteComments(rel: string): Promise { - await this.fs.deleteFile(this.commentsPathFor(rel)).catch(() => {}) - } - // ------------------------------------------------------------------- // Workspace state // ------------------------------------------------------------------- @@ -1464,7 +1483,7 @@ export class MobileVault { } } -// Pasted image naming lives in @shared/pasted-image (upstream 80303bb), which +// Pasted image naming lives in @zennotes/shared-domain/pasted-image (upstream 80303bb), which // is where the local copy that used to sit here went: the scrub of the // characters that break the `![[...]]` embed a paste writes has to agree // across desktop, web, the server and here, and one module is how it stays diff --git a/src/bridge/vault-lifecycle.test.ts b/src/bridge/vault-lifecycle.test.ts new file mode 100644 index 0000000..6f58675 --- /dev/null +++ b/src/bridge/vault-lifecycle.test.ts @@ -0,0 +1,110 @@ +import assert from 'node:assert/strict' +import { test } from 'node:test' +import { loadMobileModule } from '../../tooling/load-mobile-module.ts' + +Object.defineProperty(globalThis, 'localStorage', { configurable: true, value: { getItem: () => null } }) +const { MobileVault, NativeFs, DEFAULT_VAULT_SETTINGS } = await loadMobileModule([ + './src/bridge/vault-fs', './src/bridge/native-fs', '@zennotes/bridge-contract/ipc' +]) + +class MemoryFs { + files = new Map() + directories = new Set() + failMove: ((from: string, to: string) => boolean) | null = null + failWrite = false + async statVerified(path: string) { + return this.files.has(path) ? 'file' : this.directories.has(path) || [...this.files.keys()].some(key => key.startsWith(`${path}/`)) ? 'directory' : null + } + async exists(path: string) { return await this.statVerified(path) !== null } + async mkdir(path: string) { this.directories.add(path) } + async readText(path: string) { if (!this.files.has(path)) throw new Error('ENOENT'); return this.files.get(path)! } + async readTextOrNull(path: string) { return this.files.get(path) ?? null } + async writeText(path: string, body: string) { + if (this.failWrite) { this.failWrite = false; throw new Error('Settings write refused') } + this.files.set(path, body) + } + async deleteFile(path: string) { this.files.delete(path) } + async rmdir(path: string) { + for (const key of this.files.keys()) if (key === path || key.startsWith(`${path}/`)) this.files.delete(key) + for (const key of this.directories) if (key === path || key.startsWith(`${path}/`)) this.directories.delete(key) + } + async rename(from: string, to: string) { + if (this.failMove?.(from, to)) throw new Error('Provider refused move') + assert.ok(await this.exists(from)); assert.equal(await this.exists(to), false) + for (const [key, body] of [...this.files]) if (key === from || key.startsWith(`${from}/`)) { + this.files.set(to + key.slice(from.length), body); this.files.delete(key) + } + for (const key of [...this.directories]) if (key === from || key.startsWith(`${from}/`)) { + this.directories.add(to + key.slice(from.length)); this.directories.delete(key) + } + } +} +function fixture(rootMode = false) { + const fs = new MemoryFs(), vault = new MobileVault('Fixture') + Object.assign(vault, { + fs, settingsCache: { ...structuredClone(DEFAULT_VAULT_SETTINGS), primaryNotesLocation: rootMode ? 'root' : 'inbox', + systemFolderPaths: { inbox: 'Notes', archive: 'Old', trash: 'Bin', quick: 'Capture' } }, + listNotes: async () => [], invalidateMeta: () => {}, + metaForPath: async (path: string) => ({ path, title: path.split('/').pop() }) + }) + const note = `${rootMode ? '' : 'Notes/'}Work/One.md` + fs.files.set(note, '# One\n\nExact café 日本語. \n') + fs.files.set(`.zennotes/comments/${note}.comments.json`, '{malformed but preserved sidecar}') + return { fs, vault, note, original: new Map(fs.files) } +} +for (const rootMode of [false, true]) { + test(`native move and restore preserve bytes and sidecars with remapping (root=${rootMode})`, async () => { + const s = fixture(rootMode), result = await s.vault.moveToTrash(s.note) + assert.equal(result.path, 'Bin/Work/One.md') + assert.equal(s.fs.files.get(result.path), s.original.get(s.note)) + assert.equal(s.fs.files.get(`.zennotes/comments/${result.path}.comments.json`), '{malformed but preserved sidecar}') + const restored = await s.vault.restoreFromTrash(result.path) + assert.equal(restored.path, s.note) + assert.deepEqual(s.fs.files, s.original) + }) +} +test('native note relocation restores content when the comments provider fails', async () => { + const s = fixture() + s.fs.failMove = from => from.startsWith('.zennotes/comments/') + await assert.rejects(s.vault.moveToTrash(s.note), /Provider refused move/) + assert.deepEqual(s.fs.files, s.original) +}) +test('native folder rename moves database files and nested sidecars together', async () => { + const s = fixture() + s.fs.files.set('Notes/Work/Projects.base/data.csv', 'ID,Name\n1,One\n') + s.fs.files.set('Notes/Work/Projects.base/schema.json', '{"pages":{"1":"One.md"}}') + await s.vault.renameFolder('inbox', 'Work', 'Moved') + assert.equal(s.fs.files.get('Notes/Moved/Projects.base/data.csv'), 'ID,Name\n1,One\n') + assert.equal(s.fs.files.get('.zennotes/comments/Notes/Moved/One.md.comments.json'), '{malformed but preserved sidecar}') + assert.equal(s.fs.files.has(s.note), false) +}) +test('native folder rename restores content, comments and settings on metadata failure', async () => { + const s = fixture() + s.fs.files.set('.zennotes/vault.json', '{"keep":"exact settings"}\n') + s.original.set('.zennotes/vault.json', '{"keep":"exact settings"}\n') + s.fs.failWrite = true + await assert.rejects(s.vault.renameFolder('inbox', 'Work', 'Moved'), /Settings write refused/) + assert.deepEqual(s.fs.files, s.original) +}) +test('native Empty Trash rolls back if comments cannot detach, then deletes nested comments on retry', async () => { + const s = fixture() + await s.vault.moveToTrash(s.note) + const before = new Map(s.fs.files) + s.fs.failMove = from => from === '.zennotes/comments/Bin' + await assert.rejects(s.vault.emptyTrash(), /Provider refused move/) + assert.deepEqual(s.fs.files, before) + s.fs.failMove = null + await s.vault.emptyTrash() + assert.equal(s.fs.files.size, 0) + await s.vault.emptyTrash() +}) +test('native deletion does not report success after a failed sidecar move', async () => { + const s = fixture() + s.fs.failMove = from => from.startsWith('.zennotes/comments/') + await assert.rejects(s.vault.deleteNote(s.note), /Provider refused move/) + assert.deepEqual(s.fs.files, s.original) +}) +test('native absent-file reads propagate provider failures instead of allowing schema adoption', async () => { + const permission = new Error('Permission denied') + await assert.rejects(NativeFs.prototype.readTextOrNull.call({ readText: async () => { throw permission } }, 'schema.json'), permission) +}) diff --git a/src/bridge/widget-snapshot.ts b/src/bridge/widget-snapshot.ts index bd950ab..e3c720a 100644 --- a/src/bridge/widget-snapshot.ts +++ b/src/bridge/widget-snapshot.ts @@ -13,8 +13,8 @@ * Only pure selectors live here (node --test covers them). The store/pins * wiring and the native call are in widgets.ts. */ -import type { NoteMeta } from '@bridge-contract/ipc' -import type { VaultTask } from '@shared/tasks' +import type { NoteMeta } from '@zennotes/bridge-contract/ipc' +import type { VaultTask } from '@zennotes/shared-domain/tasks' import { parseThemeBackdropColor } from './keyboard-backdrop-color.ts' export const WIDGET_SNAPSHOT_VERSION = 1 diff --git a/src/bridge/widgets.ts b/src/bridge/widgets.ts index 2239d96..3ec8982 100644 --- a/src/bridge/widgets.ts +++ b/src/bridge/widgets.ts @@ -18,14 +18,14 @@ */ import { App as CapApp } from '@capacitor/app' import { Capacitor, registerPlugin } from '@capacitor/core' -import { useStore } from '@zennotes/app-core/store' -import { computeTasksRender } from '@zennotes/app-core/lib/tasks-filter' -import { filterTasksForDisplay, toIsoDateLocal } from '@shared/tasks' +import { getShellSnapshot, subscribeShell, type ShellSnapshot } from '@zennotes/app-core/shell' +import { getTasksSnapshot, subscribeTasks, refreshTasks, getTodayTasks, type TasksSnapshot } from '@zennotes/app-core/tasks' +import { subscribeSettings } from '@zennotes/app-core/settings' +import { toIsoDateLocal } from '@zennotes/shared-domain/tasks' import { activeVaultStateKey, isMobileNoteIndexReady } from './mobile-bridge' import { getPinnedNotes, subscribePins } from '../ui-mobile/pins' import { WIDGET_SNAPSHOT_VERSION, - filterLiveTasks, selectWidgetNotes, selectWidgetTasks, themeFromTokens, @@ -43,21 +43,12 @@ interface ZenWidgetsPlugin { export const ZenWidgets = registerPlugin('ZenWidgets') -const NO_COLLAPSE = { - today: false, - upcoming: false, - waiting: false, - forwarded: false, - done: false, - cancelled: false -} - const PUBLISH_DEBOUNCE_MS = 400 const PUBLISH_MIN_INTERVAL_MS = 8000 /** Past this many changed notes one full scan beats per-note rescans. */ const RESCAN_BATCH_LIMIT = 8 -type StoreState = ReturnType +type StoreState = ShellSnapshot let timer = 0 let lastPublishedAt = 0 @@ -72,16 +63,8 @@ function themeMode(): WidgetTheme['mode'] { function buildSnapshot(state: StoreState, now: Date): Omit { const style = getComputedStyle(document.documentElement) - const live = filterLiveTasks( - filterTasksForDisplay(state.vaultTasks, state.showArchivedTasks), - state.notes - ) - const render = computeTasksRender(live, '', now, NO_COLLAPSE) - const { tasks, counts } = selectWidgetTasks( - render.groups.today, - render.groups.overdueCount ?? 0, - toIsoDateLocal(now) - ) + const today = getTodayTasks(now) + const { tasks, counts } = selectWidgetTasks(today.tasks, today.overdueCount, toIsoDateLocal(now)) return { version: WIDGET_SNAPSHOT_VERSION, vaultName: state.vault?.name ?? null, @@ -94,10 +77,10 @@ function buildSnapshot(state: StoreState, now: Date): Omit { - const state = useStore.getState() + const state = getShellSnapshot() // No vault (onboarding, a switch in flight): keep whatever the widgets // already show rather than blanking them. - if (!state.vault) return + if (!state.vault || !state.workspaceRestored) return let body: Omit try { body = buildSnapshot(state, new Date()) @@ -129,17 +112,17 @@ function flush(): void { void publish() } -function reconcileTasks(state: StoreState, prev: StoreState | null): void { - if (!state.vault || !isMobileNoteIndexReady()) return +function reconcileTasks(state: StoreState, prev: StoreState | null, tasks: TasksSnapshot, previousTasks: TasksSnapshot | null): void { + if (!state.vault || !state.workspaceRestored || !isMobileNoteIndexReady()) return const key = activeVaultStateKey() if (key !== taskVaultKey) { taskVaultKey = key tasksSettled = false knownUpdatedAt = new Map(state.notes.map((n) => [n.path, n.updatedAt])) - if (!state.tasksLoading) void state.refreshTasks() + if (!tasks.loading) void refreshTasks() return } - if (prev && prev.tasksLoading && !state.tasksLoading) tasksSettled = true + if (previousTasks?.loading && !tasks.loading) tasksSettled = true if (!prev || state.notes === prev.notes) return const changed: string[] = [] const next = new Map() @@ -150,38 +133,37 @@ function reconcileTasks(state: StoreState, prev: StoreState | null): void { knownUpdatedAt = next if (changed.length === 0) return if (changed.length > RESCAN_BATCH_LIMIT) { - if (!state.tasksLoading) void state.refreshTasks() + if (!tasks.loading) void refreshTasks() return } - for (const path of changed) void state.rescanTasksForPath(path) + for (const path of changed) void refreshTasks(path) } /** Start publishing; returns the teardown (tests / hot paths — the shell * itself never stops). No-op off the native platform. */ export function installWidgetPublisher(): () => void { if (!Capacitor.isNativePlatform()) return () => {} - const unsubStore = useStore.subscribe((state, prev) => { - reconcileTasks(state, prev) - if ( - state.notes !== prev.notes || - state.vaultTasks !== prev.vaultTasks || - state.showArchivedTasks !== prev.showArchivedTasks || - state.tasksLoading !== prev.tasksLoading || - state.vault !== prev.vault || - state.themeId !== prev.themeId || - state.themeMode !== prev.themeMode - ) { - schedule() - } - }) + let shell = getShellSnapshot() + let tasks = getTasksSnapshot() + const changed = (): void => { + const nextShell = getShellSnapshot(), nextTasks = getTasksSnapshot() + const previousShell = shell, previousTasks = tasks + // Store subscriptions can fire synchronously when refreshTasks starts. + shell = nextShell; tasks = nextTasks + reconcileTasks(nextShell, previousShell, nextTasks, previousTasks) + schedule() + } + const unsubShell = subscribeShell(changed) + const unsubTasks = subscribeTasks(changed) + const unsubSettings = subscribeSettings(schedule) const unsubPins = subscribePins(schedule) const appState = CapApp.addListener('appStateChange', ({ isActive }) => { if (!isActive) flush() }) - reconcileTasks(useStore.getState(), null) + reconcileTasks(getShellSnapshot(), null, getTasksSnapshot(), null) schedule() return () => { - unsubStore() + unsubShell(); unsubTasks(); unsubSettings() unsubPins() void appState.then((handle) => handle.remove()).catch(() => {}) window.clearTimeout(timer) diff --git a/src/main.tsx b/src/main.tsx index d284c27..1cc03e3 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,6 +10,7 @@ */ import { App as CapApp } from '@capacitor/app' import { Keyboard, KeyboardResize } from '@capacitor/keyboard' +import { installMobileEditorHost } from './ui-mobile/editor-host' import { renderZenNotesApp } from '@zennotes/app-core/main' import { installMobileBridge, @@ -111,6 +112,7 @@ async function boot(): Promise { const root = document.getElementById('root') if (!root) throw new Error('Renderer root element #root was not found') + installMobileEditorHost() renderZenNotesApp(root) mountMobileShell() // Home Screen / Lock Screen widgets: publish what they show, and run the diff --git a/src/ui-mobile/EditorToolbar.tsx b/src/ui-mobile/EditorToolbar.tsx index c06ede0..5a9af05 100644 --- a/src/ui-mobile/EditorToolbar.tsx +++ b/src/ui-mobile/EditorToolbar.tsx @@ -2,95 +2,17 @@ * The mobile editing toolbar (spec 06's marquee input feature): a horizontally * scrollable formatting row docked above the soft keyboard while the * CodeMirror editor is focused. Actions drive the shared editor through the - * store's `editorViewRef` using app-core's own formatting helpers - * (lib/cm-format.ts) plus stock @codemirror/commands — no editor logic is - * duplicated, and no zennotes code changes. + * public semantic commands, without owning editor state or formatting logic. * * With the Capacitor Keyboard in `resize: native` mode the viewport shrinks * when the keyboard shows, so `bottom: 0` docks exactly on the keyboard's top. */ import React, { useEffect, useState } from 'react' import { Keyboard } from '@capacitor/keyboard' -import type { EditorView } from '@codemirror/view' -import { indentLess, indentMore, redo, undo } from '@codemirror/commands' -import { openSearchPanel } from '@codemirror/search' -import { EditorSelection } from '@codemirror/state' -import { useStore } from '@zennotes/app-core/store' -import { - setBlockType, - toggleWrap, - wrapLink, - type BlockType -} from '@zennotes/app-core/lib/cm-format' +import { runEditorCommand } from '@zennotes/app-core/editor' import { promptAttachFiles } from './attach' import { revealCaretAboveKeyboardSoon } from './editor-keyboard-scroll' -function view(): EditorView | null { - return useStore.getState().editorViewRef -} - -function withView(fn: (v: EditorView) => void): void { - const v = view() - if (!v) return - fn(v) - v.focus() -} - -/** Insert text at the cursor, placing the caret `caretOffset` chars in. */ -function insertSnippet(v: EditorView, text: string, caretOffset: number): void { - const { from, to } = v.state.selection.main - v.dispatch({ - changes: { from, to, insert: text }, - selection: EditorSelection.cursor(from + caretOffset) - }) -} - -/** - * Markers app-core's blockPrefix would put on these block types. app-core's - * setBlockType converts existing lines and deliberately skips blank ones, so - * on a fresh line Bullet / Checkbox / Heading did nothing until something was - * typed (Adib, device testing 2026-09-08). Desktop users just type the - * marker; on the phone the button IS the way to start a list. - */ -const BLANK_LINE_MARKERS: Partial> = { - bullet: '- ', - todo: '- [ ] ', - h1: '# ', - h2: '## ', - h3: '### ' -} - -/** - * setBlockType, plus the blank-line case it skips: a collapsed cursor on an - * empty (or whitespace-only) line gets the marker inserted after the existing - * indentation, caret after the marker. Selections and non-blank lines go - * through setBlockType unchanged. - */ -function applyBlockType(v: EditorView, type: BlockType): void { - const { from, to } = v.state.selection.main - const line = v.state.doc.lineAt(from) - const marker = BLANK_LINE_MARKERS[type] - if (marker !== undefined && from === to && line.text.trim() === '') { - // line.text is whitespace-only here, so it doubles as the indent. - const insert = line.text + marker - v.dispatch({ - changes: { from: line.from, to: line.to, insert }, - selection: EditorSelection.cursor(line.from + insert.length) - }) - return - } - setBlockType(v, type) -} - -/** Cycle the current line's heading level: none → # → ## → ### → none. */ -function cycleHeading(v: EditorView): void { - const line = v.state.doc.lineAt(v.state.selection.main.from) - const m = line.text.match(/^(#{1,6})\s/) - const level = m ? m[1]!.length : 0 - const next = level >= 3 ? 'paragraph' : (['h1', 'h2', 'h3'] as const)[level]! - applyBlockType(v, next) -} - interface ToolButton { key: string label: string @@ -105,13 +27,13 @@ const BUTTONS: ToolButton[] = [ key: 'undo', label: 'Undo', d: 'M9 14L4 9l5-5M4 9h10.5a5.5 5.5 0 015.5 5.5v0a5.5 5.5 0 01-5.5 5.5H11', - run: () => withView((v) => undo(v)) + run: () => runEditorCommand('undo') }, { key: 'redo', label: 'Redo', d: 'M15 14l5-5-5-5M20 9H9.5A5.5 5.5 0 004 14.5v0A5.5 5.5 0 009.5 20H13', - run: () => withView((v) => redo(v)) + run: () => runEditorCommand('redo') }, { key: 'find', @@ -121,10 +43,7 @@ const BUTTONS: ToolButton[] = [ // withView's editor refocus would immediately steal it back. Focus moves // input-to-input, so the keyboard stays up (Discord feedback, 2026-08-20: // "I have to exit the note to search for a word"). - run: () => { - const v = view() - if (v) openSearchPanel(v) - } + run: () => { runEditorCommand('open-search') } }, { key: 'attach', @@ -138,84 +57,84 @@ const BUTTONS: ToolButton[] = [ key: 'todo', label: 'Checkbox', d: 'M9 11l3 3L22 4M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11', - run: () => withView((v) => applyBlockType(v, 'todo')) + run: () => runEditorCommand('set-task-list') }, { key: 'bullet', label: 'Bullet list', d: 'M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01', - run: () => withView((v) => applyBlockType(v, 'bullet')) + run: () => runEditorCommand('set-bullet-list') }, { key: 'heading', label: 'Heading', glyph: 'H', d: '', - run: () => withView((v) => cycleHeading(v)) + run: () => runEditorCommand('cycle-heading') }, { key: 'bold', label: 'Bold', glyph: 'B', d: '', - run: () => withView((v) => toggleWrap(v, '**')) + run: () => runEditorCommand('toggle-bold') }, { key: 'italic', label: 'Italic', glyph: 'I', d: '', - run: () => withView((v) => toggleWrap(v, '*')) + run: () => runEditorCommand('toggle-italic') }, { key: 'strike', label: 'Strikethrough', d: 'M16 4H9a3 3 0 00-2.83 4M14 12a4 4 0 010 8H6M4 12h16', - run: () => withView((v) => toggleWrap(v, '~~')) + run: () => runEditorCommand('toggle-strikethrough') }, { key: 'highlight', label: 'Highlight', d: 'M12 20h9M16.5 3.5a2.1 2.1 0 013 3L7 19l-4 1 1-4z', - run: () => withView((v) => toggleWrap(v, '==')) + run: () => runEditorCommand('toggle-highlight') }, { key: 'code', label: 'Inline code', d: 'M16 18l6-6-6-6M8 6l-6 6 6 6', - run: () => withView((v) => toggleWrap(v, '`')) + run: () => runEditorCommand('toggle-inline-code') }, { key: 'link', label: 'Link', d: 'M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71', - run: () => withView((v) => wrapLink(v)) + run: () => runEditorCommand('insert-link') }, { key: 'wikilink', label: 'Wikilink', glyph: '[[', d: '', - run: () => withView((v) => insertSnippet(v, '[[]]', 2)) + run: () => runEditorCommand('insert-wikilink') }, { key: 'tag', label: 'Tag', glyph: '#', d: '', - run: () => withView((v) => insertSnippet(v, '#', 1)) + run: () => runEditorCommand('insert-tag') }, { key: 'outdent', label: 'Outdent', d: 'M11 8h10M11 12h10M11 16h10M7 8l-4 4 4 4', - run: () => withView((v) => indentLess(v)) + run: () => runEditorCommand('outdent') }, { key: 'indent', label: 'Indent', d: 'M11 8h10M11 12h10M11 16h10M3 8l4 4-4 4', - run: () => withView((v) => indentMore(v)) + run: () => runEditorCommand('indent') } ] diff --git a/src/ui-mobile/MobileDrawer.tsx b/src/ui-mobile/MobileDrawer.tsx index 09e44d9..e6ec809 100644 --- a/src/ui-mobile/MobileDrawer.tsx +++ b/src/ui-mobile/MobileDrawer.tsx @@ -1,3 +1,4 @@ +import { dirOf } from './note-order' /** * The phone navigation drawer — a purpose-built mobile surface that REPLACES * app-core's desktop sidebar below 768px (which is hidden by CSS). Flat, @@ -7,23 +8,20 @@ */ import React, { useEffect, useMemo, useRef, useState } from 'react' import ReactDOM from 'react-dom/client' -import { useStore } from '@zennotes/app-core/store' -import type { NoteSortOrder } from '@zennotes/app-core/store' -import { confirmApp } from '@zennotes/app-core/lib/confirm-requests' -import { promptApp } from '@zennotes/app-core/lib/prompt-requests' -import { notePathWithinFolder } from '@zennotes/app-core/lib/vault-layout' -import { resolveFolderPath } from '@zennotes/shared-domain/system-folder-paths' -import { - csvPathForFormDir, - databaseTabPath, - FORM_DIR_SUFFIX, - isFormDirName -} from '@zennotes/shared-domain/databases' +import { getShellSnapshot, useShellSnapshot, setNoteSortOrder, type NoteSortOrder } from '@zennotes/app-core/shell' +import { getBrowseSnapshot, useBrowseSnapshot, getBrowseDirectory, requestCreateBrowseFolder, + requestRenameBrowseFolder, requestDeleteBrowseDirectory } from '@zennotes/app-core/browse' +import { useWorkspaceSnapshot, openLocalVault, pickLocalVault, refreshRemoteProfiles, connectRemoteWorkspace, + connectRemoteProfile, changeRemoteVaultPath, deleteRemoteProfile } from '@zennotes/app-core/workspace' +import { openNote, openAppPage } from '@zennotes/app-core/navigation' +import { showSearch } from '@zennotes/app-core/commands' +import { setSettingsVisible } from '@zennotes/app-core/settings' +import { confirm as confirmApp, prompt as promptApp } from '@zennotes/app-core/dialogs' +import { captureMobileWorkspace, reportActionError } from './workspace-context' import { Keyboard } from '@capacitor/keyboard' import { setDrawerOpen, takeDrawerPath, useDrawerOpen } from './drawer-state' import { openMobileSheet } from './sheet-state' import { goHome } from './nav' -import { dirOf, noteComparator, pinnedFirst } from './note-order' import { usePins, toggleNotePin, toggleFolderPin } from './pins' import { archiveNote, openNoteMenu, trashNote } from './note-actions' import { refreshVault } from './refresh' @@ -178,9 +176,7 @@ function NewVaultSheet({ tier === 'icloud' ? `${ICLOUD_VAULT_ROOT_PREFIX}${encodeURIComponent(clean)}` : `${VAULT_ROOT_PREFIX}${clean}` - useStore - .getState() - .openLocalVault(root) + openLocalVault(root) .then(() => onDone(true)) .catch((err) => { setError(String((err as Error)?.message ?? err)) @@ -197,12 +193,10 @@ function NewVaultSheet({ setBusy('pick') setError('') dismissKeyboard() - const before = useStore.getState().vault?.root ?? null - useStore - .getState() - .openVaultPicker() + const before = getShellSnapshot().vault?.root ?? null + pickLocalVault() .then(() => { - const after = useStore.getState().vault?.root ?? null + const after = getShellSnapshot().vault?.root ?? null if (after !== before) onDone(true) else setBusy(null) }) @@ -335,10 +329,8 @@ const TIER_SECTIONS = [ ] as const export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Element { - const currentName = useStore((s) => s.vault?.name ?? null) - const workspaceMode = useStore((s) => s.workspaceMode) - const remoteProfileId = useStore((s) => s.remoteWorkspaceInfo?.profileId ?? null) - const remoteProfiles = useStore((s) => s.remoteWorkspaceProfiles) + const currentName = useShellSnapshot().vault?.name ?? null + const { mode: workspaceMode, remoteProfileId, remoteProfiles } = useWorkspaceSnapshot() const [entries, setEntries] = useState(null) const [view, setView] = useState({ kind: 'list' }) const [busy, setBusy] = useState(null) @@ -357,7 +349,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele void icloudStatus() .then((s) => setCloudOk(Boolean(s.available && s.rootUrl))) .catch(() => {}) - void useStore.getState().refreshRemoteWorkspaceProfiles() + void refreshRemoteProfiles() }, []) // The storage pref tracks whichever tier is open (every switch path sets @@ -398,35 +390,23 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele }) } - const tokenFor = (tier: 'local' | 'icloud', name: string): string => - tier === 'icloud' - ? `${ICLOUD_VAULT_ROOT_PREFIX}${encodeURIComponent(name)}` - : `${VAULT_ROOT_PREFIX}${name}` - const submitRename = (entry: MobileVaultEntry): void => { if (entry.tier === 'external') return - const tier = entry.tier const clean = sanitizeNoteTitle(renameTo.trim()) dismissKeyboard() if (!clean || clean === entry.name) { setView({ kind: 'vault', entry }) return } - const wasCurrent = isCurrent(entry) manage('rename', async () => { await renameVault(entry, clean) - // Renaming the open vault: route the store through its normal switch so - // the whole workspace picks up the new identity. - if (wasCurrent) await useStore.getState().openLocalVault(tokenFor(tier, clean)) }) } const moveEntry = (entry: MobileVaultEntry, to: 'local' | 'icloud'): void => { if (entry.tier === 'external') return - const wasCurrent = isCurrent(entry) manage('move', async () => { await moveVault(entry, to) - if (wasCurrent) await useStore.getState().openLocalVault(tokenFor(to, entry.name)) }) } @@ -444,7 +424,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele onClose() // Let the sheet unmount so the guided URL/token prompts get focus. window.setTimeout(() => { - void useStore.getState().connectRemoteWorkspace() + void connectRemoteWorkspace() }, 30) } @@ -505,7 +485,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele disabled={current} onClick={() => act(`switch:${entry.root}`, () => - useStore.getState().openLocalVault(entry.root) + openLocalVault(entry.root) ) } > @@ -546,7 +526,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele disabled={current} onClick={() => act(`switch:${profile.id}`, () => - useStore.getState().connectRemoteWorkspaceProfile(profile.id) + connectRemoteProfile(profile.id) ) } > @@ -614,7 +594,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele className="zn-mobile-sheet-row" onClick={() => act(`switch:${view.entry.root}`, () => - useStore.getState().openLocalVault(view.entry.root) + openLocalVault(view.entry.root) ) } > @@ -779,7 +759,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele className="zn-mobile-sheet-row" onClick={() => act(`switch:${view.id}`, () => - useStore.getState().connectRemoteWorkspaceProfile(view.id) + connectRemoteProfile(view.id) ) } > @@ -796,7 +776,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele // Server-side folder browser renders as a modal — leave // the sheet first so it gets focus. window.setTimeout(() => { - void useStore.getState().changeRemoteWorkspaceVaultPath() + void changeRemoteVaultPath() }, 30) }} > @@ -809,7 +789,7 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele className="zn-mobile-sheet-row zn-danger" onClick={() => manage('remove', () => - useStore.getState().deleteRemoteWorkspaceProfile(view.id) + deleteRemoteProfile(view.id) ) } > @@ -831,32 +811,13 @@ export function VaultsSheet({ onClose }: { onClose: () => void }): React.JSX.Ele export function MobileDrawer(): React.JSX.Element | null { const open = useDrawerOpen() - const vaultName = useStore((s) => s.vault?.name ?? 'ZenNotes') - // The store subscription is only the change signal (root flips on every - // vault switch); pins key on the bridge's STABLE identity token — - // `vault.root` itself is friendlyVaultRoot()'s presentation copy, which a - // wording tweak would change, orphaning every pin (activeVaultStateKey). - const vaultRoot = useStore((s) => s.vault?.root ?? null) - const pinKey = useMemo(() => (vaultRoot ? activeVaultStateKey() : null), [vaultRoot]) + const browse = useBrowseSnapshot() + const vaultName = browse.vault?.name ?? 'ZenNotes' + const vaultRoot = browse.vault?.root ?? null + const pinKey = vaultRoot ? activeVaultStateKey() : null const pins = usePins(pinKey) - const notes = useStore((s) => s.notes) - const folders = useStore((s) => s.folders) - const primaryAtRoot = useStore((s) => s.vaultSettings.primaryNotesLocation === 'root') - // Primitive selectors only — returning a fresh object from a selector - // re-renders forever (Object.is on a new Set is never equal). - const dailyDir = useStore((s) => - s.vaultSettings.dailyNotes.enabled ? s.vaultSettings.dailyNotes.directory : null - ) - const weeklyDir = useStore((s) => - s.vaultSettings.weeklyNotes.enabled ? s.vaultSettings.weeklyNotes.directory : null - ) - const monthlyDir = useStore((s) => - s.vaultSettings.monthlyNotes.enabled ? s.vaultSettings.monthlyNotes.directory : null - ) - const noteSortOrder = useStore((s) => s.noteSortOrder) - // Stable reference from the store (replaced wholesale on settings reload), - // so this is selector-safe; needed for remap-aware path composition. - const vaultSettings = useStore((s) => s.vaultSettings) + const { daily: dailyDir, weekly: weeklyDir, monthly: monthlyDir } = browse.dateDirectories + const noteSortOrder = browse.noteSortOrder const dateDirs = useMemo(() => { const dirs = new Set() if (dailyDir) dirs.add(dailyDir) @@ -873,45 +834,13 @@ export function MobileDrawer(): React.JSX.Element | null { }, [open]) const { childFolders, childDatabases, childNotes } = useMemo(() => { - const inboxDir = resolveFolderPath('inbox', vaultSettings.systemFolderPaths) - const folderSet = new Map() - const databases: Array<[string, string, string]> = [] - for (const f of folders) { - if (f.folder !== 'inbox') continue - if (dirOf(f.subpath) !== path) continue - const name = f.subpath.split('/').pop() ?? f.subpath - if (isFormDirName(name)) { - // Databases are `.base` folders — surface them as openable rows. - const vaultRel = primaryAtRoot ? f.subpath : `${inboxDir}/${f.subpath}` - databases.push([ - databaseTabPath(csvPathForFormDir(vaultRel)), - name.slice(0, -FORM_DIR_SUFFIX.length), - f.subpath - ]) - continue - } - folderSet.set(f.subpath, name) - } - const noteRows = notes - .filter((n) => { - if (n.folder !== 'inbox') return false - const sub = notePathWithinFolder(n.path, 'inbox', vaultSettings) - return dirOf(sub) === path && !isFormDirName(dirOf(sub).split('/').pop() ?? '') - }) - .sort(noteComparator(noteSortOrder)) - // Pinned rows float to the top of their group, keeping the sort order - // within each half (pins.ts). - const pinnedNoteSet = new Set(pins.notes) - const pinnedFolderSet = new Set(pins.folders) + const rows = getBrowseDirectory(browse, path, pins) return { - childFolders: pinnedFirst( - [...folderSet.entries()].sort((a, b) => a[1].localeCompare(b[1])), - ([subpath]) => pinnedFolderSet.has(subpath) - ), - childDatabases: databases.sort((a, b) => a[1].localeCompare(b[1])), - childNotes: pinnedFirst(noteRows, (n) => pinnedNoteSet.has(n.path)) + childFolders: rows.folders.map(row => [row.directory, row.title] as [string, string]), + childDatabases: rows.databases.map(row => [row.path, row.title, row.directory] as [string, string, string]), + childNotes: [...rows.notes] } - }, [notes, folders, path, primaryAtRoot, noteSortOrder, vaultSettings, pins]) + }, [browse, path, pins]) if (!open) return null @@ -925,7 +854,6 @@ export function MobileDrawer(): React.JSX.Element | null { window.setTimeout(() => void action(), 30) } - const s = (): ReturnType => useStore.getState() return ( <> @@ -946,7 +874,6 @@ export function MobileDrawer(): React.JSX.Element | null { noteSortOrder={noteSortOrder} close={close} go={go} - s={s} onOpenVaults={() => openMobileSheet('vaults')} /> @@ -1118,7 +1045,6 @@ function MobileDrawerBody(props: { noteSortOrder: NoteSortOrder close: () => void go: (action: () => unknown) => void - s: () => ReturnType }): React.JSX.Element { const { vaultName, @@ -1136,25 +1062,24 @@ function MobileDrawerBody(props: { childNotes, noteSortOrder, close, - go, - s + go } = props const lp = useLongPress() const [sortOpen, setSortOpen] = useState(false) // Long-pressing a row opens its action sheet — the phone's right-click - // (Discord folder feedback, ported from the Android shell). Notes open the - // shell-wide note sheet (note-actions.tsx, shared with app-core's lists); - // folders get Rename/Delete here. Prompts overlay the open drawer (Modal - // layers above it), so the drawer stays put and its list refreshes in - // place via the vault change events. - const [folderMenu, setFolderMenu] = useState<{ subpath: string; name: string } | null>(null) + // (Discord folder feedback). Notes open the shell-wide note sheet + // (note-actions.tsx, shared with app-core's lists); folders get + // Rename/Delete here. Prompts overlay the open drawer (Modal layers above + // z-49), so the drawer stays put and its list refreshes in place via the + // vault change events. + const [folderMenu, setFolderMenu] = useState<{ subpath: string; name: string; host: ReturnType } | null>(null) const pinNote = (notePath: string): void => { if (!pinKey) return toggleNotePin( pinKey, notePath, - s().notes.map((n) => n.path) + getShellSnapshot().notes.map((n) => n.path) ) } @@ -1163,9 +1088,7 @@ function MobileDrawerBody(props: { toggleFolderPin( pinKey, subpath, - s() - .folders.filter((f) => f.folder === 'inbox') - .map((f) => f.subpath) + getBrowseSnapshot().folders.map(row => row.directory) ) } @@ -1176,63 +1099,20 @@ function MobileDrawerBody(props: { const scrollRef = useRef(null) - const renameFolderFromDrawer = (subpath: string, name: string): void => { + const renameFolderFromDrawer = (subpath: string, _name: string): void => { + const host = folderMenu?.host ?? captureMobileWorkspace() setFolderMenu(null) - void (async () => { - const next = await promptApp({ - title: 'Rename folder', - initialValue: name, - okLabel: 'Rename', - validate: (v: string) => (v.includes('/') ? 'Folder name cannot contain "/"' : null) - }) - const clean = next?.trim() - if (!clean || clean === name) return - const parent = subpath.includes('/') ? subpath.slice(0, subpath.lastIndexOf('/')) : '' - try { - await s().renameFolder('inbox', subpath, parent ? `${parent}/${clean}` : clean) - } catch (err) { - window.alert(err instanceof Error ? err.message : String(err)) - } - })() + void requestRenameBrowseFolder(host, subpath).catch(reportActionError) } - const newFolderHere = (): void => { - void (async () => { - const leaf = path === '' ? '' : (path.split('/').pop() ?? '') - const name = await promptApp({ - title: leaf ? `New folder in ${leaf}` : 'New folder', - placeholder: 'Folder name', - okLabel: 'Create', - validate: (v: string) => (v.includes('/') ? 'Folder name cannot contain "/"' : null) - }) - const clean = name?.trim().replace(/^\/+|\/+$/g, '') - if (!clean) return - await s().createFolder('inbox', path === '' ? clean : `${path}/${clean}`) - })() + void requestCreateBrowseFolder(captureMobileWorkspace(), path).catch(reportActionError) } - - const deleteDatabase = (subpath: string, title: string): void => { - void (async () => { - const ok = await confirmApp({ - title: `Delete "${title}"?`, - description: 'All records will be permanently deleted. This cannot be undone.', - confirmLabel: 'Delete', - danger: true - }) - if (ok) await s().deleteFolder('inbox', subpath) - })() + const deleteDatabase = (subpath: string, _title: string): void => { + void requestDeleteBrowseDirectory(captureMobileWorkspace(), subpath).catch(reportActionError) } - - const deleteFolder = (subpath: string, name: string): void => { - void (async () => { - const ok = await confirmApp({ - title: `Delete "${name}"?`, - description: 'Everything inside will be permanently deleted. This cannot be undone.', - confirmLabel: 'Delete', - danger: true - }) - if (ok) await s().deleteFolder('inbox', subpath) - })() + const deleteFolder = (subpath: string, _name: string): void => { + const host = folderMenu?.host ?? captureMobileWorkspace() + void requestDeleteBrowseDirectory(host, subpath).catch(reportActionError) } return ( @@ -1251,7 +1131,7 @@ function MobileDrawerBody(props: { - @@ -1266,11 +1146,11 @@ function MobileDrawerBody(props: { Home - - @@ -1295,22 +1175,22 @@ function MobileDrawerBody(props: { )} - {/* The assets table only exists as a pane tab (zen://assets) — the palette's "Go to Files" drives the desktop sidebar list, which phones don't render, so this row is the phone's way in. */} - - - @@ -1350,7 +1230,7 @@ function MobileDrawerBody(props: { aria-checked={noteSortOrder === order} className={noteSortOrder === order ? 'is-active' : ''} onClick={() => { - s().setNoteSortOrder(order) + setNoteSortOrder(order) setSortOpen(false) }} > @@ -1373,7 +1253,7 @@ function MobileDrawerBody(props: { diff --git a/src/ui-mobile/MobileShell.tsx b/src/ui-mobile/MobileShell.tsx index 30d9f04..351f247 100644 --- a/src/ui-mobile/MobileShell.tsx +++ b/src/ui-mobile/MobileShell.tsx @@ -6,44 +6,24 @@ * full-screen flow. Rendered into its own React root so app-core stays * untouched; state is driven through the shared Zustand store. */ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useMemo, useState } from 'react' import ReactDOM from 'react-dom/client' import { Haptics, ImpactStyle } from '@capacitor/haptics' import { Keyboard } from '@capacitor/keyboard' -import { useStore } from '@zennotes/app-core/store' -import { - getGesturePrefs, - setGesturePrefs, - type GesturePrefs, - type PullAction, - type SwipeAction -} from './gestures' -import type { TaskMutation } from '@zennotes/app-core/store' -import type { VaultTask } from '@shared/tasks' -import { toIsoDateLocal } from '@shared/tasks' -import { buildCommands } from '@zennotes/app-core/lib/commands' -import { findLeaf, updateLeaf } from '@zennotes/app-core/lib/pane-layout' -import { - paneModeForPath, - paneModesWithPathMode, - requestPaneMode -} from '@zennotes/app-core/lib/pane-mode' -import { - isSameFileHeadingLink, - resolveWikilinkTarget, - wikilinkHeadingAnchor -} from '@zennotes/app-core/lib/wikilinks' -import { - openDatabaseFromWikilink, - openWikilinkHeading -} from '@zennotes/app-core/lib/wikilink-navigation' +import { getShellSnapshot, useShellSnapshot, subscribeShell, getAdjacentNotePath, + getTagPresenceSnapshot, subscribeTagPresence } from '@zennotes/app-core/shell' +import { getBrowseSnapshot, requestCreateBrowseFolder, requestDeleteBrowseDirectory } from '@zennotes/app-core/browse' +import { getWorkspaceSnapshot, useWorkspaceSnapshot, subscribeWorkspace, readPersistedHomeState, + configureWorkspacePresentation, pickLocalVault, openLocalVault, connectRemoteProfile, refreshRemoteProfiles } from '@zennotes/app-core/workspace' +import { getSettingsSnapshot, useSettingsSnapshot, subscribeSettings, setSettingsVisible, setEditorFontSize } from '@zennotes/app-core/settings' +import { useEditorPresentation, setEditorMode, hasEditorSelection, runEditorCommand } from '@zennotes/app-core/editor' +import { getTasksSnapshot, moveTaskToColumn, type KanbanGroupBy } from '@zennotes/app-core/tasks' +import { runAppCommand, showCommandPalette, showSearch, showTemplates, showOutline } from '@zennotes/app-core/commands' +import { openNote, openWikilink, openTodayDailyNote } from '@zennotes/app-core/navigation' +import { requestTrashNote } from '@zennotes/app-core/notes' +import { getGesturePrefs, setGesturePrefs, type GesturePrefs, type PullAction, type SwipeAction } from './gestures' import { MobileEditorToolbar } from './EditorToolbar' -import { promptApp } from '@zennotes/app-core/lib/prompt-requests' -import { confirmApp } from '@zennotes/app-core/lib/confirm-requests' -import { notePathWithinFolder } from '@zennotes/app-core/lib/vault-layout' -import { noteTagsForCount } from '@zennotes/app-core/lib/tags' -import { resolveTypstPreambleFolder } from '@zennotes/app-core/lib/typst-preamble' -import { csvPathFromDatabaseTab, formDirFromCsvPath } from '@zennotes/shared-domain/databases' +import { captureMobileWorkspace, reportActionError } from './workspace-context' import { MobileDrawer } from './MobileDrawer' import { isDrawerOpen, setDrawerOpen, useDrawerOpen } from './drawer-state' import { goHome } from './nav' @@ -67,11 +47,6 @@ import { WELCOME_PENDING_KEY, FAB_HINT_KEY } from './Onboarding' import { WELCOME_NOTE_PATH } from '../bridge/welcome-note' import ensoUrl from '../assets/enso.png' import { getStoragePref } from '../bridge/icloud' -import { - createTagsEmptyStateTracker, - type TagsEmptyStateSnapshot -} from './tags-empty-state' -import { siblingNotesInDrawerOrder } from './note-order' import { getPinnedNotes, loadPins } from './pins' import { isSwipeRowGestureActive } from './SwipeRow' import { installNoteRowGestures, NOTE_ROW_SELECTOR } from './note-row-gestures' @@ -94,10 +69,8 @@ import { /** Run a command from the shared registry by id (same path the palette uses). */ function runCommand(id: string): void { - const cmd = buildCommands({ includeUnavailable: true }).find((c) => c.id === id) - if (!cmd) return - if (cmd.when && !cmd.when()) return - void cmd.run() + void runAppCommand(id).catch(reportActionError) + } function Icon({ d, filled }: { d: string; filled?: boolean }): React.JSX.Element { @@ -194,56 +167,22 @@ const APP_ROWS: SheetRow[] = [ ] function ActionSheet({ onClose }: { onClose: () => void }): React.JSX.Element { - const selectedPath = useStore((s) => s.selectedPath) - const workspaceMode = useStore((s) => s.workspaceMode) - // Virtual tabs (zen://help, zen://tasks, ...) aren't notes — their rows - // (rename/trash/...) would silently no-op. - const hasNote = Boolean(selectedPath) && !selectedPath?.startsWith('zen://') - const noteFolder = useStore((s) => { - if (!s.selectedPath) return null - return s.notes.find((n) => n.path === s.selectedPath)?.folder ?? null - }) - const calendarAvailable = useStore( - (s) => s.vaultSettings.dailyNotes.enabled || s.vaultSettings.weeklyNotes.enabled - ) - // With a database tab open, offer its removal — every open thing should be - // deletable from •••. - const dbFormDir = useStore((s) => { - const csv = csvPathFromDatabaseTab(s.selectedPath) - return csv ? formDirFromCsvPath(csv) : null - }) - const dbTitle = dbFormDir - ? (dbFormDir.split('/').pop() ?? '').replace(/\.base$/i, '') - : null - const title = useStore((s) => { - if (!s.selectedPath) return 'ZenNotes' - const note = s.notes.find((n) => n.path === s.selectedPath) - return note?.title ?? 'ZenNotes' - }) - + const shell = useShellSnapshot() + const { selectedPath, workspaceMode } = shell + const hasNote = !!shell.selectedNote + const noteFolder = shell.selectedNote?.folder ?? null + const { calendarAvailable } = useSettingsSnapshot() + const database = getBrowseSnapshot().databases.find(row => row.path === selectedPath) + const dbFormDir = database?.directory ?? null + const dbTitle = database?.title ?? null + const title = shell.selectedNote?.title ?? 'ZenNotes' + const workspace = useWorkspaceSnapshot() + const host = useMemo(() => captureMobileWorkspace(), [shell.vault, workspaceMode, workspace.generation, workspace.transitioning]) const deleteOpenDatabase = (): void => { - const formDir = dbFormDir - const label = dbTitle - if (!formDir || label === null) return + if (!dbFormDir) return onClose() window.setTimeout(() => { - void (async () => { - const ok = await confirmApp({ - title: `Delete "${label}"?`, - description: 'All records will be permanently deleted. This cannot be undone.', - confirmLabel: 'Delete', - danger: true - }) - if (!ok) return - // Remap-aware: the inbox may live in a renamed directory - // (vault.json systemFolderPaths), so strip the RESOLVED prefix. - const subpath = notePathWithinFolder( - formDir, - 'inbox', - useStore.getState().vaultSettings - ) - await useStore.getState().deleteFolder('inbox', subpath) - })() + void requestDeleteBrowseDirectory(host, dbFormDir).catch(reportActionError) }, 30) } @@ -253,7 +192,7 @@ function ActionSheet({ onClose }: { onClose: () => void }): React.JSX.Element { // focus lands in the right place. window.setTimeout(() => { if (id === 'zn.palette') { - useStore.getState().setCommandPaletteOpen(true) + showCommandPalette() return } if (id === 'zn.vaults') { @@ -261,26 +200,12 @@ function ActionSheet({ onClose }: { onClose: () => void }): React.JSX.Element { return } if (id === 'zn.pickfolder') { - void useStore.getState().openVaultPicker() + void pickLocalVault() return } + if (!host.isCurrent()) return if (id === 'note.trash') { - // Own the confirm copy ("Delete") — app-core's command would show its - // desktop "Move to Trash?" dialog. The bridge's unlink event closes - // the tab via applyChange. - void (async () => { - const st = useStore.getState() - const path = st.selectedPath - if (!path) return - const noteTitle = st.notes.find((n) => n.path === path)?.title - const ok = await confirmApp({ - title: `Delete "${noteTitle ?? 'this note'}"?`, - description: 'It will move to the trash.', - confirmLabel: 'Delete', - danger: true - }) - if (ok) await window.zen.moveToTrash(path) - })() + if (selectedPath) void requestTrashNote(host, selectedPath).catch(reportActionError) return } runCommand(id) @@ -377,22 +302,14 @@ function ActionSheet({ onClose }: { onClose: () => void }): React.JSX.Element { * quick-action chips are hidden on phones, so this is its one-tap home. */ function CreateSheet({ onClose }: { onClose: () => void }): React.JSX.Element { - const dailyEnabled = useStore((s) => s.vaultSettings.dailyNotes.enabled) + const dailyEnabled = useSettingsSnapshot().dailyNotesEnabled const run = (fn: () => unknown): void => { onClose() window.setTimeout(() => void fn(), 30) } const newFolder = async (): Promise => { - const name = await promptApp({ - title: 'New folder', - placeholder: 'Folder name', - okLabel: 'Create', - validate: (v: string) => (v.includes('/') ? 'Folder name cannot contain "/"' : null) - }) - const clean = name?.trim().replace(/^\/+|\/+$/g, '') - if (!clean) return - await useStore.getState().createFolder('inbox', clean) + await requestCreateBrowseFolder(captureMobileWorkspace(), '').catch(reportActionError) } return ( @@ -422,7 +339,7 @@ function CreateSheet({ onClose }: { onClose: () => void }): React.JSX.Element {