diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..5d58e9c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,56 @@
+name: Bug report
+description: Something broke in Parcel
+title: "[Bug]: "
+labels: [bug]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for reporting. Include macOS version and whether Screen Recording was granted **after** install (quit + reopen required).
+ - type: textarea
+ id: what
+ attributes:
+ label: What happened?
+ description: Steps to reproduce, if known.
+ validations:
+ required: true
+ - type: textarea
+ id: expected
+ attributes:
+ label: What did you expect?
+ validations:
+ required: true
+ - type: dropdown
+ id: area
+ attributes:
+ label: Area
+ options:
+ - Capture / Overlay
+ - Editor / Annotations
+ - Recording
+ - Vision / OCR
+ - Upload
+ - Preferences / Hotkeys
+ - Updates / Onboarding
+ - Other
+ validations:
+ required: true
+ - type: input
+ id: macos
+ attributes:
+ label: macOS version
+ placeholder: e.g. macOS 26.0
+ validations:
+ required: true
+ - type: input
+ id: build
+ attributes:
+ label: Parcel build
+ placeholder: Debug from source / Release zip / Homebrew
+ - type: checkboxes
+ id: permission
+ attributes:
+ label: Screen Recording
+ options:
+ - label: Granted in System Settings, and I quit/reopened Parcel after granting
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..27331f8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Website
+ url: https://parcel-zeta-silk.vercel.app
+ about: Marketing site, download, and guides
+ - name: Parable ecosystem
+ url: https://github.com/bswxyz/parable
+ about: UI components and templates from the same author
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 0000000..fa8e465
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,32 @@
+name: Feature request
+description: Suggest an improvement to Parcel
+title: "[Feature]: "
+labels: [enhancement]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Describe the problem and proposed solution. Parcel stays **local-first** β features that require network AI will not be accepted.
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem or use case
+ description: Who is this for and when does it come up?
+ validations:
+ required: true
+ - type: textarea
+ id: proposal
+ attributes:
+ label: Proposed solution
+ description: How should it work in the Editor, Overlay, or menu bar?
+ validations:
+ required: true
+ - type: checkboxes
+ id: constraints
+ attributes:
+ label: Constraints acknowledged
+ options:
+ - label: Stays on-device (no network AI / LLM)
+ required: true
+ - label: Uses canonical glossary (Capture, Annotation, Tool, β¦)
+ required: false
diff --git a/.github/assets/capture-flow.svg b/.github/assets/capture-flow.svg
new file mode 100644
index 0000000..d64d399
--- /dev/null
+++ b/.github/assets/capture-flow.svg
@@ -0,0 +1,29 @@
+
+
+ CAPTURE PIPELINE
+
+
+ Freeze
+ ScreenCaptureKit
+
+
+
+ Select
+ Overlay
+
+
+
+ Editor
+ Annotate
+
+
+
+ Ship
+ Copy · Save
+
+
+ RENDER PIPELINE
+ raw Capture CI adjustments censor derive Canvas Beautify export
+ on screen === saved
+ Undo covers annotations only · Adjustments & Beautify are document settings
+
diff --git a/.github/assets/hero.svg b/.github/assets/hero.svg
new file mode 100644
index 0000000..03437f2
--- /dev/null
+++ b/.github/assets/hero.svg
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NATIVE MACOS · MIT · FROM PARABLE
+ Capture, mark up,
+ and ship.
+ Local-first. No network AI. On screen = saved.
+
+
+
+
+
+
+
+ parcel.parable.dev/editor
+
+
+
+
+
+
+
+ 14 tools · Beautify · Vision · History
+
+
diff --git a/.github/assets/toolkit.svg b/.github/assets/toolkit.svg
new file mode 100644
index 0000000..b199a0d
--- /dev/null
+++ b/.github/assets/toolkit.svg
@@ -0,0 +1,22 @@
+
+
+ TOOLKIT
+
+
+
+
+
+
+
+ Select Arrow Rect
+ Ellipse Text Pencil
+ Censor Number Stamp
+ Highlight Measure Spotlight
+
+
+ Also included
+ Scroll capture · Screen recording · Capture history
+ Vision OCR / QR / faces · Local PII censor · Optional Supabase upload
+ Beautify (30 gradients) · Brand kits · PNG/JPEG/HEIC/TIFF export
+ 0 cloud AI calls
+
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..e89e0c5
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,29 @@
+## Summary
+
+
+
+## Type
+
+- [ ] Bug fix
+- [ ] Feature
+- [ ] Docs / website
+- [ ] Refactor
+- [ ] Release / CI
+
+## Test plan
+
+
+
+- [ ] Built Debug locally (`xcodegen generate` + `xcodebuild`)
+- [ ] Ran relevant items from [docs/QA_CHECKLIST.md](docs/QA_CHECKLIST.md)
+- [ ] Website builds (`cd Website && npm run build`) β if Website/ changed
+
+## Constraints
+
+- [ ] No network AI / LLM calls added
+- [ ] Glossary terms preserved (Capture, Selection, Overlay, Editor, Annotation, Tool, Canvas, Layer)
+- [ ] Render pipeline unchanged or intentionally updated with docs
+
+## Screenshots / recordings
+
+
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..f5bfdf2
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,41 @@
+name: Build
+
+on:
+ push:
+ branches: [main, master]
+ pull_request:
+
+jobs:
+ build:
+ runs-on: macos-15
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install XcodeGen
+ run: brew install xcodegen
+
+ - name: Generate project
+ run: xcodegen generate
+
+ - name: Resolve packages
+ run: xcodebuild -resolvePackageDependencies -project Parcel.xcodeproj -scheme Parcel
+
+ - name: Build Debug
+ run: |
+ xcodebuild -project Parcel.xcodeproj -scheme Parcel -configuration Debug \
+ -derivedDataPath .derivedData build \
+ CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=YES
+
+ build-website:
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: Website
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "22"
+ cache: npm
+ cache-dependency-path: Website/package-lock.json
+ - run: npm ci && npm run build
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..fa79e4a
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,42 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - "v*"
+
+jobs:
+ release:
+ runs-on: macos-15
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install XcodeGen
+ run: brew install xcodegen
+
+ - name: Build, export, notarize
+ env:
+ DEVELOPMENT_TEAM: ${{ secrets.DEVELOPMENT_TEAM }}
+ APPLE_ID: ${{ secrets.APPLE_ID }}
+ APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
+ run: |
+ chmod +x Scripts/release.sh
+ ./Scripts/release.sh
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ files: build/Parcel.zip
+ generate_release_notes: true
+
+ - name: Build website
+ working-directory: Website
+ run: |
+ npm ci
+ npm run build
+
+ - name: Upload website artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: website-dist
+ path: Website/out
diff --git a/.gitignore b/.gitignore
index b270cdf..cdbb176 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,7 @@ xcuserdata/
## XcodeGen β the project is generated from project.yml.
## Regenerate locally with `xcodegen generate`.
-Notable.xcodeproj/
+Parcel.xcodeproj/
## Swift Package Manager
.build/
@@ -30,3 +30,6 @@ Package.resolved
## Misc
*.log
+.derivedData-release/
+.vercel-deploy*.json
+.cursor/
diff --git a/AGENTS.md b/AGENTS.md
index 7f6f5f3..271b6bb 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,11 +1,11 @@
-# Notable β Build Guide & Fixed Decisions
+# Parcel β Build Guide & Fixed Decisions
-Notable is an **original, from-scratch macOS screenshot / annotation / (later) recording app**
-in SwiftUI. It is **not** a fork of anything. The open-source app *macshot* is used **only** as a
-feature-parity reference β no code, assets, naming, or glossary terms are copied from it.
+Parcel is an **original, from-scratch macOS Capture / annotation / recording app** from the
+Parable ecosystem, built in SwiftUI. It is **not** a fork of anything. The open-source app
+*macshot* is used **only** as a feature-parity reference β no code, assets, naming, or glossary
+terms are copied from it.
-> "Notable" is a working codename. It collides with an existing note-taking app, so a final
-> original name + icon is required before any public repo. Do not treat the name as settled.
+**Bundle ID:** `dev.parable.Parcel` Β· **Display name:** Parcel
---
@@ -42,21 +42,10 @@ as a class name (use **Annotation**), *crop box* (use **Selection**), *drawing/m
## Current phase
-**Phases 1β3 and local Phase 4: IMPLEMENTED and verified (Debug build on macOS 26).** macOS 13
-fallback capture/recording paths remain written-but-untested on this machine.
-Phase 1 (capture β annotate β copy/save loop) is complete. Phase 2 adds: Arrow Γ5 styles,
-Censor Γ3 modes (blur/pixelate/solid), Number/Stamp/Highlighter/Measure/Spotlight Tools,
-restyle-existing (selection-aware toolbar, coalesced undo), Loupe + Eyedropper utility Tools,
-Beautify (30 gradients, padding, radius, shadow, window chrome), and on-device Core Image
-Adjustments (7 params + presets).
-
-Phase 3 adds: manual scroll Capture (Vision registration plus sampled pixel-overlap validation),
-all-display stitched Capture, delay/aspect/boundary Capture controls, ScreenCaptureKit MP4 recording,
-trim, and GIF/MP4 export. The recorder uses the native ScreenCaptureKit output on macOS 15+ and an
-AVFoundation stream writer fallback on macOS 13β14.
-
-Local Phase 4 adds: re-editable disk-backed history, Vision OCR/QR/face detection, regex-guided
-local PII Censoring, face Censoring, PNG/JPEG/HEIC/TIFF output, and local Beautify brand kits.
+**Phases 1β4: IMPLEMENTED.** v1.0 Release build on macOS 26 with sandbox, Sparkle, onboarding,
+configurable hotkey, Supabase upload (user-configured), ellipse Tool, erase censor mode, and
+on-device translation (macOS 15+). macOS 13 capture/recording fallbacks remain written-but-untested
+on the primary dev machine β see `docs/MACOS13_VM_QA.md`.
**Phase 2 render pipeline (the load-bearing design β do not regress):**
raw Capture β adjusted base (CI chain; neutral short-circuits to raw) β censor blur/pixelate
@@ -71,12 +60,11 @@ ImageRenderer at capture.scale. One composition drives display AND export ("on s
**Undo scope:** the βZ stack covers annotation content ONLY (create/delete/move/resize/restyle).
Adjustments and Beautify are document-level settings, out of the stack, with panel Resets.
-**Still not implemented:** translation, smart erase, WebP/AVIF codecs, Supabase upload/short links
-and analytics, sync, cross-device handoff, capture templates, iOS/iPadOS companion, and a
-**production** marketing site. A draft static download page exists in `Website/` (local Debug zip,
-codename branding β not signed/notarized). Supabase work requires a project URL, auth policy,
-bucket name, and final branded domain; never ship a pretend one-click upload without those real
-values.
+**Still not implemented (v1.1+):** WebP/AVIF codecs, cloud history sync, analytics, cross-device
+handoff, capture templates, iOS/iPadOS companion, App Store submission.
+
+**Release infra:** `Scripts/release.sh`, GitHub Actions, Next.js site at `Website/`, Sparkle appcast
+at `Website/public/appcast.xml`. Manual QA matrix: `docs/QA_CHECKLIST.md`.
---
@@ -85,7 +73,7 @@ values.
- **Global hotkey: Carbon `RegisterEventHotKey`** (`Hotkeys/HotKeyManager.swift`), not `CGEventTap`.
It is the mechanism `KeyboardShortcuts`/`HotKey` use and needs **no Accessibility permission** β
eliminating a whole class of "permissions dead-end." Default: **ββ§2** (ββ§3/4/5 are macOS system
- shortcuts). Configurable UI is a later task; the binding lives in one place for now.
+ shortcuts). **Configurable in Preferences** via `HotKeyRecorder`.
- **Capture model: freeze-then-select.** On hotkey we capture a full-res image of every display
via ScreenCaptureKit, show it frozen in the Overlay, and crop the Selection from those frozen
pixels. No live-overlay window exclusion needed; gives the "frozen screen" feel.
@@ -97,21 +85,19 @@ values.
(`Capture/OverlayWindow.swift` + `OverlayController.swift`). One panel per display. Esc cancels
via a local key monitor; drag = region Selection; single click on a highlighted window = window snap.
- **Menu bar: SwiftUI `MenuBarExtra`** + `LSUIElement` (no dock icon). App activation policy is
- `.accessory`. Menu bar icon is an SF Symbol placeholder β **flagged for a custom icon pass**.
+ `.accessory`. Custom menu bar icon in `Assets.xcassets/MenuBarIcon`.
- **Editor / Preferences windows: AppKit-managed `NSWindow` + `NSHostingView`** (not SwiftUI
`WindowGroup`/`Settings`), because we open them programmatically with a captured image payload
and want explicit lifecycle control on 13.0+.
## Sandbox
-Phase 1 dev builds are **non-sandboxed** (`Resources/Notable.entitlements` has
-`com.apple.security.app-sandbox = false`). Reason: Screen Recording TCC permission is keyed to the
-code signature and churns on every ad-hoc-signed rebuild under the sandbox β that would break
-"usable daily today." Hardening checklist before any public release:
-1. Set `com.apple.security.app-sandbox` = `true`.
-2. Add `com.apple.security.files.user-selected.read-write` (Save panel).
-3. Re-test that ScreenCaptureKit + the Save panel still work under sandbox.
-4. Adopt a stable signing identity so the TCC grant persists across rebuilds.
+Release builds are **sandboxed** (`Resources/Parcel.entitlements`):
+- `com.apple.security.app-sandbox` = `true`
+- `com.apple.security.files.user-selected.read-write` for Save panel
+
+Use a **stable Developer ID** signing identity so Screen Recording TCC persists across updates.
+Debug ad-hoc builds may need permission re-grant after rebuild.
## Concurrency
@@ -125,8 +111,9 @@ Swift 6 language mode once the surface is stable.
```
project.yml XcodeGen spec β the .xcodeproj is generated, not committed.
-Sources/Notable/
- App/ NotableApp (@main), AppDelegate, AppCoordinator (app state + wiring)
+Sources/Parcel/
+ App/ ParcelApp (@main), AppDelegate, AppCoordinator, AppIdentity migration
+ Onboarding/ WelcomeWindowController (first-run flow)
MenuBar/ MenuBarContent (SwiftUI menu)
Hotkeys/ HotKeyManager (Carbon global hotkey)
Permissions/ ScreenRecordingPermission (TCC check/request + guidance)
@@ -135,18 +122,24 @@ Sources/Notable/
Editor/ EditorModel, EditorWindowController, EditorView, output/beautify/adjustment support
History/ Local re-editable Capture document store and History window
Recording/ ScreenCaptureKit recorder, macOS 13 AVFoundation fallback, trim/export window
- Vision/ On-device OCR, QR, face, and local redaction analysis
- Preferences/ PreferencesWindowController, PreferencesView
- Support/ Extensions (NSScreen/βCGRect helpers)
- Resources/ Info.plist, Notable.entitlements, Assets.xcassets
+ Vision/ On-device OCR, QR, face, translation, and local redaction analysis
+ Upload/ Supabase Storage upload (optional, user-configured)
+ Preferences/ PreferencesWindowController, PreferencesView, HotKeyRecorder
+ Support/ Extensions, AppIdentity
+ Resources/ Info.plist, Parcel.entitlements, PrivacyInfo.xcprivacy, Assets.xcassets
+Scripts/ release.sh, generate_icons.sh, ExportOptions.plist
+Website/ Next.js marketing site (parcel.parable.dev)
+docs/ QA_CHECKLIST.md, MACOS13_VM_QA.md
```
### Build
```
-xcodegen generate # regenerate Notable.xcodeproj from project.yml (after adding/moving files)
-xcodebuild -project Notable.xcodeproj -scheme Notable -configuration Debug build
+xcodegen generate # regenerate Parcel.xcodeproj from project.yml
+xcodebuild -project Parcel.xcodeproj -scheme Parcel -configuration Debug build
```
-Run the built `.app` (from DerivedData) directly; on first Capture, grant **Screen Recording** in
-System Settings β Privacy & Security, then reopen Notable.
+Run the built `.app` (from DerivedData) directly; on first launch complete onboarding and grant
+**Screen Recording** in System Settings β Privacy & Security, then reopen Parcel.
+
+Release: `DEVELOPMENT_TEAM=β¦ ./Scripts/release.sh`
diff --git a/CLAUDE.md b/CLAUDE.md
index 18e2485..271b6bb 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,17 +1,17 @@
-# Notable β Build Guide & Fixed Decisions
+# Parcel β Build Guide & Fixed Decisions
-Notable is an **original, from-scratch macOS screenshot / annotation / (later) recording app**
-in SwiftUI. It is **not** a fork of anything. The open-source app *macshot* is used **only** as a
-feature-parity reference β no code, assets, naming, or glossary terms are copied from it.
+Parcel is an **original, from-scratch macOS Capture / annotation / recording app** from the
+Parable ecosystem, built in SwiftUI. It is **not** a fork of anything. The open-source app
+*macshot* is used **only** as a feature-parity reference β no code, assets, naming, or glossary
+terms are copied from it.
-> "Notable" is a working codename. It collides with an existing note-taking app, so a final
-> original name + icon is required before any public repo. Do not treat the name as settled.
+**Bundle ID:** `dev.parable.Parcel` Β· **Display name:** Parcel
---
## Hard constraints (non-negotiable β do not relitigate)
-1. **No LLM / network AI calls anywhere.** Not Claude, not OpenAI, nothing that sends data
+1. **No LLM / network AI calls anywhere.** Not Codex, not OpenAI, nothing that sends data
off-device for AI. On-device Apple frameworks only (Vision; local Apple Intelligence APIs
only if stable on-device). Any "AI" feature must be provably local.
2. **macOS 13.0+**, Apple Silicon + Intel.
@@ -42,21 +42,10 @@ as a class name (use **Annotation**), *crop box* (use **Selection**), *drawing/m
## Current phase
-**Phases 1β3 and local Phase 4: IMPLEMENTED and verified (Debug build on macOS 26).** macOS 13
-fallback capture/recording paths remain written-but-untested on this machine.
-Phase 1 (capture β annotate β copy/save loop) is complete. Phase 2 adds: Arrow Γ5 styles,
-Censor Γ3 modes (blur/pixelate/solid), Number/Stamp/Highlighter/Measure/Spotlight Tools,
-restyle-existing (selection-aware toolbar, coalesced undo), Loupe + Eyedropper utility Tools,
-Beautify (30 gradients, padding, radius, shadow, window chrome), and on-device Core Image
-Adjustments (7 params + presets).
-
-Phase 3 adds: manual scroll Capture (Vision registration plus sampled pixel-overlap validation),
-all-display stitched Capture, delay/aspect/boundary Capture controls, ScreenCaptureKit MP4 recording,
-trim, and GIF/MP4 export. The recorder uses the native ScreenCaptureKit output on macOS 15+ and an
-AVFoundation stream writer fallback on macOS 13β14.
-
-Local Phase 4 adds: re-editable disk-backed history, Vision OCR/QR/face detection, regex-guided
-local PII Censoring, face Censoring, PNG/JPEG/HEIC/TIFF output, and local Beautify brand kits.
+**Phases 1β4: IMPLEMENTED.** v1.0 Release build on macOS 26 with sandbox, Sparkle, onboarding,
+configurable hotkey, Supabase upload (user-configured), ellipse Tool, erase censor mode, and
+on-device translation (macOS 15+). macOS 13 capture/recording fallbacks remain written-but-untested
+on the primary dev machine β see `docs/MACOS13_VM_QA.md`.
**Phase 2 render pipeline (the load-bearing design β do not regress):**
raw Capture β adjusted base (CI chain; neutral short-circuits to raw) β censor blur/pixelate
@@ -71,12 +60,11 @@ ImageRenderer at capture.scale. One composition drives display AND export ("on s
**Undo scope:** the βZ stack covers annotation content ONLY (create/delete/move/resize/restyle).
Adjustments and Beautify are document-level settings, out of the stack, with panel Resets.
-**Still not implemented:** translation, smart erase, WebP/AVIF codecs, Supabase upload/short links
-and analytics, sync, cross-device handoff, capture templates, iOS/iPadOS companion, and a
-**production** marketing site. A draft static download page exists in `Website/` (local Debug zip,
-codename branding β not signed/notarized). Supabase work requires a project URL, auth policy,
-bucket name, and final branded domain; never ship a pretend one-click upload without those real
-values.
+**Still not implemented (v1.1+):** WebP/AVIF codecs, cloud history sync, analytics, cross-device
+handoff, capture templates, iOS/iPadOS companion, App Store submission.
+
+**Release infra:** `Scripts/release.sh`, GitHub Actions, Next.js site at `Website/`, Sparkle appcast
+at `Website/public/appcast.xml`. Manual QA matrix: `docs/QA_CHECKLIST.md`.
---
@@ -85,7 +73,7 @@ values.
- **Global hotkey: Carbon `RegisterEventHotKey`** (`Hotkeys/HotKeyManager.swift`), not `CGEventTap`.
It is the mechanism `KeyboardShortcuts`/`HotKey` use and needs **no Accessibility permission** β
eliminating a whole class of "permissions dead-end." Default: **ββ§2** (ββ§3/4/5 are macOS system
- shortcuts). Configurable UI is a later task; the binding lives in one place for now.
+ shortcuts). **Configurable in Preferences** via `HotKeyRecorder`.
- **Capture model: freeze-then-select.** On hotkey we capture a full-res image of every display
via ScreenCaptureKit, show it frozen in the Overlay, and crop the Selection from those frozen
pixels. No live-overlay window exclusion needed; gives the "frozen screen" feel.
@@ -97,21 +85,19 @@ values.
(`Capture/OverlayWindow.swift` + `OverlayController.swift`). One panel per display. Esc cancels
via a local key monitor; drag = region Selection; single click on a highlighted window = window snap.
- **Menu bar: SwiftUI `MenuBarExtra`** + `LSUIElement` (no dock icon). App activation policy is
- `.accessory`. Menu bar icon is an SF Symbol placeholder β **flagged for a custom icon pass**.
+ `.accessory`. Custom menu bar icon in `Assets.xcassets/MenuBarIcon`.
- **Editor / Preferences windows: AppKit-managed `NSWindow` + `NSHostingView`** (not SwiftUI
`WindowGroup`/`Settings`), because we open them programmatically with a captured image payload
and want explicit lifecycle control on 13.0+.
## Sandbox
-Phase 1 dev builds are **non-sandboxed** (`Resources/Notable.entitlements` has
-`com.apple.security.app-sandbox = false`). Reason: Screen Recording TCC permission is keyed to the
-code signature and churns on every ad-hoc-signed rebuild under the sandbox β that would break
-"usable daily today." Hardening checklist before any public release:
-1. Set `com.apple.security.app-sandbox` = `true`.
-2. Add `com.apple.security.files.user-selected.read-write` (Save panel).
-3. Re-test that ScreenCaptureKit + the Save panel still work under sandbox.
-4. Adopt a stable signing identity so the TCC grant persists across rebuilds.
+Release builds are **sandboxed** (`Resources/Parcel.entitlements`):
+- `com.apple.security.app-sandbox` = `true`
+- `com.apple.security.files.user-selected.read-write` for Save panel
+
+Use a **stable Developer ID** signing identity so Screen Recording TCC persists across updates.
+Debug ad-hoc builds may need permission re-grant after rebuild.
## Concurrency
@@ -125,8 +111,9 @@ Swift 6 language mode once the surface is stable.
```
project.yml XcodeGen spec β the .xcodeproj is generated, not committed.
-Sources/Notable/
- App/ NotableApp (@main), AppDelegate, AppCoordinator (app state + wiring)
+Sources/Parcel/
+ App/ ParcelApp (@main), AppDelegate, AppCoordinator, AppIdentity migration
+ Onboarding/ WelcomeWindowController (first-run flow)
MenuBar/ MenuBarContent (SwiftUI menu)
Hotkeys/ HotKeyManager (Carbon global hotkey)
Permissions/ ScreenRecordingPermission (TCC check/request + guidance)
@@ -135,18 +122,24 @@ Sources/Notable/
Editor/ EditorModel, EditorWindowController, EditorView, output/beautify/adjustment support
History/ Local re-editable Capture document store and History window
Recording/ ScreenCaptureKit recorder, macOS 13 AVFoundation fallback, trim/export window
- Vision/ On-device OCR, QR, face, and local redaction analysis
- Preferences/ PreferencesWindowController, PreferencesView
- Support/ Extensions (NSScreen/βCGRect helpers)
- Resources/ Info.plist, Notable.entitlements, Assets.xcassets
+ Vision/ On-device OCR, QR, face, translation, and local redaction analysis
+ Upload/ Supabase Storage upload (optional, user-configured)
+ Preferences/ PreferencesWindowController, PreferencesView, HotKeyRecorder
+ Support/ Extensions, AppIdentity
+ Resources/ Info.plist, Parcel.entitlements, PrivacyInfo.xcprivacy, Assets.xcassets
+Scripts/ release.sh, generate_icons.sh, ExportOptions.plist
+Website/ Next.js marketing site (parcel.parable.dev)
+docs/ QA_CHECKLIST.md, MACOS13_VM_QA.md
```
### Build
```
-xcodegen generate # regenerate Notable.xcodeproj from project.yml (after adding/moving files)
-xcodebuild -project Notable.xcodeproj -scheme Notable -configuration Debug build
+xcodegen generate # regenerate Parcel.xcodeproj from project.yml
+xcodebuild -project Parcel.xcodeproj -scheme Parcel -configuration Debug build
```
-Run the built `.app` (from DerivedData) directly; on first Capture, grant **Screen Recording** in
-System Settings β Privacy & Security, then reopen Notable.
+Run the built `.app` (from DerivedData) directly; on first launch complete onboarding and grant
+**Screen Recording** in System Settings β Privacy & Security, then reopen Parcel.
+
+Release: `DEVELOPMENT_TEAM=β¦ ./Scripts/release.sh`
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..06f24e5
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,54 @@
+# Contributing to Parcel
+
+Thank you for helping improve Parcel. This is an MIT-licensed, SwiftUI-first, **local-first** macOS app β part of the [Parable](https://github.com/bswxyz/parable) ecosystem.
+
+## Before you start
+
+1. Read [AGENTS.md](AGENTS.md) for glossary, architecture constraints, and build decisions.
+2. Skim [docs/architecture.md](docs/architecture.md) if your change touches Capture, Editor, or export.
+3. For Capture/recording/Vision changes, plan to run [docs/QA_CHECKLIST.md](docs/QA_CHECKLIST.md).
+
+**Non-negotiable:** no network LLM or AI calls. On-device Apple frameworks only.
+
+## Setup
+
+```sh
+brew install xcodegen
+git clone https://github.com/bswxyz/notable.git
+cd notable
+xcodegen generate
+```
+
+## Build & run
+
+```sh
+xcodebuild -project Parcel.xcodeproj -scheme Parcel -configuration Debug \
+ -derivedDataPath .derivedData build CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=YES
+open .derivedData/Build/Products/Debug/Parcel.app
+```
+
+Grant **Screen Recording** on first Capture, then quit and reopen Parcel.
+
+## Website (optional)
+
+```sh
+cd Website && npm ci && npm run dev
+```
+
+## Pull requests
+
+- Keep diffs focused β one concern per PR when possible.
+- Match existing naming and patterns in the surrounding code.
+- Use canonical glossary terms (Capture, not screenshot; Annotation, not shape).
+- Do not regress the render pipeline: **on screen === saved**.
+- Undo stack covers **annotations only** β Adjustments and Beautify stay out of βZ.
+
+Fill out the [pull request template](.github/pull_request_template.md).
+
+## Issues
+
+Use the [bug report](.github/ISSUE_TEMPLATE/bug_report.yml) or [feature request](.github/ISSUE_TEMPLATE/feature_request.yml) templates. Include macOS version and whether Screen Recording was granted after install.
+
+## Code of conduct
+
+Be direct, be kind, be precise. Parcel is a craft project β good prose in UI copy and docs matters as much as good Swift.
diff --git a/Casks/notable.rb b/Casks/notable.rb
new file mode 100644
index 0000000..1fadb11
--- /dev/null
+++ b/Casks/notable.rb
@@ -0,0 +1,17 @@
+cask "notable" do
+ version "0.1.0"
+ sha256 :no_check
+
+ url "https://github.com/bswxyz/notable/releases/download/v#{version}/Notable.zip"
+ name "Notable"
+ desc "Local-first macOS Capture, annotation, and recording app"
+ homepage "https://github.com/bswxyz/notable"
+
+ depends_on macos: ">= :ventura"
+
+ app "Notable.app"
+
+ zap trash: [
+ "~/Library/Application Support/io.notable.Notable",
+ ]
+end
diff --git a/Casks/parcel.rb b/Casks/parcel.rb
new file mode 100644
index 0000000..46e9bff
--- /dev/null
+++ b/Casks/parcel.rb
@@ -0,0 +1,18 @@
+cask "parcel" do
+ version "1.0.0"
+ sha256 :no_check
+
+ url "https://github.com/bswxyz/notable/releases/download/v#{version}/Parcel.zip"
+ name "Parcel"
+ desc "Local-first macOS Capture, annotation, and recording app from Parable"
+ homepage "https://parcel.parable.dev"
+
+ depends_on macos: ">= :ventura"
+
+ app "Parcel.app"
+
+ zap trash: [
+ "~/Library/Application Support/dev.parable.Parcel",
+ "~/Library/Application Support/io.notable.Notable",
+ ]
+end
diff --git a/LICENSE b/LICENSE
index 2f0794d..d3d54aa 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2026 Notable Project Contributors
+Copyright (c) 2026 Parcel Project Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 1fc9364..5ae1c42 100644
--- a/README.md
+++ b/README.md
@@ -1,87 +1,174 @@
-# Notable
-
-A native macOS Capture and annotation tool, built from scratch in SwiftUI. Capture a region or a
-window, mark it up, and get it onto your clipboard or saved as a file β fast, on-device, no cloud.
-
-> **Working codename.** "Notable" is a placeholder; a final original name + icon is pending.
-
-## Status
-
-**Phase 1 β core loop: verified (Debug build).** Hotkey capture β frozen Overlay β region /
-window-snap Selection β Editor β copy / save. Builds cleanly on macOS 26; Screen Recording
-permission required for live Capture.
-
-**Phase 2 β annotation depth + Beautify: verified (code + build).** Arrow Γ5 styles, Censor Γ3
-modes, Number, Stamp, Highlighter, Measure, Spotlight, Loupe, Eyedropper, selection-aware
-restyling, Beautify (30 gradients), and on-device Core Image Adjustments. Editor βC/βS/βZ work
-from canvas focus via a local key monitor.
-
-**Phase 3 and local Phase 4: verified (code + build).** Manual Vision-assisted scroll stitching,
-MP4 display recording with trim and GIF export, local editable history (including output format),
-Vision OCR/QR/face detection, local PII/face Censoring, PNG/JPEG/HEIC/TIFF output, and saved
-Beautify brand kits.
-
-**Audit fixes applied:** user-visible errors for failed all-display stitch, empty Selection crop,
-and history restore/create failures; delay Capture countdown in the menu bar; scroll Capture
-guidance text; `outputFormat` persisted in history documents.
-
-**Known limitations:** macOS 13 ScreenCaptureKit/AVFoundation fallbacks are written but untested
-on this machine; menu bar icon is still an SF Symbol placeholder; hotkey is fixed at ββ§2; scroll
-Capture is menu-bar-driven (no in-overlay UI).
-
-**No network AI, ever** β a hard project constraint. Any later AI-adjacent feature must stay
-provably on-device through Apple frameworks.
-
-## Prompt audit
-
-Already built from the attached prompt:
-
-- Menu bar app shell, no dock icon, default **ββ§2** global Capture hotkey.
-- ScreenCaptureKit freeze-then-select flow with one Overlay per display.
-- Region Selection, window hover highlight, one-click window snap, aspect presets, and edge snap.
-- Delayed Capture and a stitched all-display Capture.
-- Editor with editable Annotations: Arrow, Rectangle, Text, Pencil, Censor, Number, Stamp,
- Highlighter, Measure, and Spotlight.
-- Utility Tools: Loupe and Eyedropper.
-- Undo / redo for Annotation content, including move, resize, delete, create, and restyle.
-- Copy to clipboard and PNG/JPEG/HEIC/TIFF save with remembered last-used folder.
-- Beautify frame: 30 gradients, padding, corner radius, shadow, and window chrome.
-- On-device Core Image Adjustments with presets.
-- Local named Beautify brand kits and a re-editable local Capture history.
-- On-device Vision OCR, QR reading, face detection, and optional sensitive-text/face Censoring.
-- Manual scroll Capture with Vision registration plus pixel-overlap validation.
-- MP4 display recording (system audio; microphone/click highlights on macOS 15+), trim, MP4 export,
- and local GIF export.
-
-Remaining later-phase work from the master prompt:
-
-- Final original name and icon before any public release.
-- Full interactive QA pass on a Mac with Screen Recording granted (Capture, scroll stitch,
- recording/audio, exports, history restore) β automated GUI testing was blocked by TCC in CI.
-- Translation, smart erase, and WebP/AVIF codecs.
-- Supabase configuration, authenticated short-link upload, analytics, and optional history sync.
-- Cross-device handoff / iOS-iPadOS companion app.
-- Production marketing site deployment (a **draft static site** lives in [`Website/`](Website/) with
- a local Debug zip β not signed, not notarized, codename branding).
-
-## Requirements
-
-- macOS 13.0+
-- Xcode 15+ (developed against Xcode 26)
+
+
+
Parcel
+
+
+ The native macOS Capture studio from Parable .
+ Freeze your screen, annotate with fourteen tools, censor with on-device Vision, beautify for ship-ready output,
+ record, scroll-capture, and upload β when you choose. No cloud AI, ever.
+
+
+
+
+
+
+
+
+
+
+
+ Website
+ Β·
+ Download
+ Β·
+ Docs
+ Β·
+ Architecture
+ Β·
+ Contributing
+
+
+
+
+
+
+
+
+
+
+## Why Parcel
+
+| | |
+|---|---|
+| **Native, not a web wrapper** | SwiftUI + ScreenCaptureKit + AppKit where structurally required. Menu bar app with a global hotkey β no Electron, no dock icon clutter. |
+| **Private by default** | OCR, face detection, translation, and regex redaction use Apple on-device frameworks only. Upload is optional Supabase Storage you configure yourself. |
+| **On screen = saved** | One render pipeline drives the Editor display and export. What you see in Beautify and Adjustments is exactly what copies or saves. |
+| **Built for daily work** | Freeze-then-select Capture, window snap, scroll stitch, MP4 recording, re-editable history, configurable hotkeys, and Sparkle updates. |
+
+
+
+## See it in action
+
+
+
+
+
+## Quick start
+
+### Requirements
+
+- macOS **13.0+** (Apple Silicon + Intel)
+- Xcode **15+** (developed on Xcode 26)
- [XcodeGen](https://github.com/yonaskolb/XcodeGen) β `brew install xcodegen`
-## Build & run
+### Build & run
```sh
xcodegen generate
-xcodebuild -project Notable.xcodeproj -scheme Notable -configuration Debug \
- -derivedDataPath .derivedData build
-open .derivedData/Build/Products/Debug/Notable.app
+xcodebuild -project Parcel.xcodeproj -scheme Parcel -configuration Debug \
+ -derivedDataPath .derivedData build CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=YES
+open .derivedData/Build/Products/Debug/Parcel.app
```
-Notable lives in the menu bar (no dock icon). Press **ββ§2** to capture. On first capture, grant
-**Screen Recording** in System Settings β Privacy & Security, then reopen Notable.
+Parcel lives in the **menu bar**. On first launch, complete the welcome flow, grant **Screen Recording**, then **quit and reopen**.
+
+Default Capture hotkey: **ββ§2** (configurable in Preferences).
+
+
+
+## Release
+
+Signed, notarized builds via [`Scripts/release.sh`](Scripts/release.sh):
+
+```sh
+DEVELOPMENT_TEAM=XXXXXXXXXX \
+APPLE_ID=you@example.com \
+APPLE_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx \
+./Scripts/release.sh
+```
+
+Set `SKIP_NOTARIZE=1` for unsigned local Release builds. Tag `v*` triggers [`.github/workflows/release.yml`](.github/workflows/release.yml).
+
+
+
+## Project layout
+
+```
+Sources/Parcel/
+ App/ Coordinator, lifecycle, onboarding
+ Capture/ ScreenCaptureKit freeze, Overlay, scroll stitch
+ Editor/ Tools, Canvas, Beautify, Adjustments, export
+ History/ Disk-backed re-editable Capture documents
+ Recording/ MP4 + trim + GIF export
+ Vision/ On-device OCR, faces, QR, translation
+ Upload/ Supabase Storage REST client
+ Hotkeys/ Carbon global hotkey + Preferences
+Website/ Next.js marketing site (parcel.parable.dev)
+docs/ Architecture, parity, QA, integrations
+Scripts/ release.sh, generate_icons.sh
+Casks/ Homebrew cask (parcel.rb)
+```
+
+The `.xcodeproj` is **generated** from [`project.yml`](project.yml) β run `xcodegen generate` after adding or moving files.
+
+
+
+## Docs
+
+| Doc | What it covers |
+|-----|----------------|
+| [docs/README.md](docs/README.md) | Documentation index |
+| [docs/architecture.md](docs/architecture.md) | Render pipeline, coordinates, module map |
+| [docs/parity.md](docs/parity.md) | macshot feature parity checklist |
+| [docs/QA_CHECKLIST.md](docs/QA_CHECKLIST.md) | Manual verification matrix |
+| [docs/integrations.md](docs/integrations.md) | GitHub, Vercel, Supabase, MCP setup |
+| [AGENTS.md](AGENTS.md) | Glossary, constraints, build guide for agents |
+
+
+
+## Built with
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Contributing
+
+Issues and pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) first β especially the glossary in [AGENTS.md](AGENTS.md) and the QA checklist for Capture/recording changes.
+
+**Hard rule:** no network LLM / AI calls. On-device Apple frameworks only.
+
+
## License
-MIT β see [LICENSE](LICENSE).
+Parcel is released under the [MIT License](LICENSE) β free and open. Built by [Parable](https://parable.dev).
+
+
+
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..927f790
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,34 @@
+# Security policy
+
+## Supported versions
+
+| Version | Supported |
+|---------|-----------|
+| Latest release on `master` / tagged `v*` | Yes |
+| Older tags | Best effort |
+
+## Reporting a vulnerability
+
+**Please do not open a public GitHub issue for security-sensitive reports.**
+
+Email or DM the maintainer with:
+
+1. Description of the issue and impact
+2. Steps to reproduce
+3. Affected macOS version and Parcel build (Debug vs Release)
+4. Proof-of-concept if available
+
+Parcel is a local-first menu bar app. Areas of interest:
+
+- ScreenCaptureKit / TCC permission handling
+- Supabase upload credentials storage (UserDefaults β user-provided keys only)
+- Sandbox entitlements in Release builds
+- Sparkle update signature verification
+
+We aim to acknowledge reports within **72 hours** and ship fixes in the next tagged release when confirmed.
+
+## Out of scope
+
+- Issues requiring physical access to an unlocked Mac
+- Missing Screen Recording permission (expected TCC behavior)
+- Ad-hoc Debug builds without notarization (dev builds are unsigned by design)
diff --git a/Scripts/ExportOptions.plist b/Scripts/ExportOptions.plist
new file mode 100644
index 0000000..bdd607e
--- /dev/null
+++ b/Scripts/ExportOptions.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ method
+ developer-id
+ signingStyle
+ automatic
+ teamID
+ $(DEVELOPMENT_TEAM)
+
+
diff --git a/Scripts/generate_icons.sh b/Scripts/generate_icons.sh
new file mode 100755
index 0000000..a9ef43a
--- /dev/null
+++ b/Scripts/generate_icons.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+SRC="${1:-$ROOT/Scripts/parcel-icon-1024.png}"
+ICONSET="$ROOT/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset"
+MENUBAR="$ROOT/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset"
+
+if [[ ! -f "$SRC" ]]; then
+ echo "Source icon not found: $SRC" >&2
+ exit 1
+fi
+
+mkdir -p "$ICONSET" "$MENUBAR"
+
+gen() {
+ local px="$1" out="$2"
+ sips -z "$px" "$px" "$SRC" --out "$out" >/dev/null
+}
+
+gen 16 "$ICONSET/icon_16x16.png"
+gen 32 "$ICONSET/icon_16x16@2x.png"
+gen 32 "$ICONSET/icon_32x32.png"
+gen 64 "$ICONSET/icon_32x32@2x.png"
+gen 128 "$ICONSET/icon_128x128.png"
+gen 256 "$ICONSET/icon_128x128@2x.png"
+gen 256 "$ICONSET/icon_256x256.png"
+gen 512 "$ICONSET/icon_512x512.png"
+gen 512 "$ICONSET/icon_256x256@2x.png"
+gen 512 "$ICONSET/icon_512x512.png"
+gen 1024 "$ICONSET/icon_512x512@2x.png"
+gen 36 "$MENUBAR/MenuBarIcon@2x.png"
+gen 18 "$MENUBAR/MenuBarIcon.png"
+
+cat > "$ICONSET/Contents.json" <<'JSON'
+{
+ "images" : [
+ { "filename" : "icon_16x16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" },
+ { "filename" : "icon_16x16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" },
+ { "filename" : "icon_32x32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" },
+ { "filename" : "icon_32x32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" },
+ { "filename" : "icon_128x128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" },
+ { "filename" : "icon_128x128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" },
+ { "filename" : "icon_256x256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" },
+ { "filename" : "icon_256x256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" },
+ { "filename" : "icon_512x512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" },
+ { "filename" : "icon_512x512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" }
+ ],
+ "info" : { "author" : "xcode", "version" : 1 }
+}
+JSON
+
+cat > "$MENUBAR/Contents.json" <<'JSON'
+{
+ "images" : [
+ { "filename" : "MenuBarIcon.png", "idiom" : "mac", "scale" : "1x" },
+ { "filename" : "MenuBarIcon@2x.png", "idiom" : "mac", "scale" : "2x" }
+ ],
+ "info" : { "author" : "xcode", "version" : 1 },
+ "properties" : { "template-rendering-intent" : "template" }
+}
+JSON
+
+echo "Generated AppIcon and MenuBarIcon assets."
diff --git a/Scripts/parcel-icon-1024.png b/Scripts/parcel-icon-1024.png
new file mode 100644
index 0000000..cd35662
Binary files /dev/null and b/Scripts/parcel-icon-1024.png differ
diff --git a/Scripts/release.sh b/Scripts/release.sh
new file mode 100755
index 0000000..be6470b
--- /dev/null
+++ b/Scripts/release.sh
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+# Archive, export, notarize, and package Parcel for public download.
+#
+# Required environment variables:
+# DEVELOPMENT_TEAM β Apple Developer Team ID
+# APPLE_ID β Apple ID for notarytool (optional if skipping notarize)
+# APPLE_APP_PASSWORD β app-specific password (optional)
+#
+# Usage:
+# DEVELOPMENT_TEAM=XXXXXXXXXX ./Scripts/release.sh
+# SKIP_NOTARIZE=1 DEVELOPMENT_TEAM=XXXXXXXXXX ./Scripts/release.sh # local test build
+set -euo pipefail
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$ROOT"
+
+SCHEME="Parcel"
+CONFIG="Release"
+ARCHIVE_PATH="$ROOT/build/Parcel.xcarchive"
+EXPORT_PATH="$ROOT/build/export"
+APP_PATH="$EXPORT_PATH/Parcel.app"
+ZIP_PATH="$ROOT/build/Parcel.zip"
+WEBSITE_ZIP="$ROOT/Website/public/downloads/Parcel.zip"
+
+if [[ -z "${DEVELOPMENT_TEAM:-}" ]]; then
+ echo "Set DEVELOPMENT_TEAM to your Apple Developer Team ID." >&2
+ exit 1
+fi
+
+echo "==> Generating Xcode project"
+xcodegen generate
+
+echo "==> Resolving Swift packages"
+xcodebuild -resolvePackageDependencies -project Parcel.xcodeproj -scheme "$SCHEME"
+
+echo "==> Archiving ($CONFIG)"
+xcodebuild archive \
+ -project Parcel.xcodeproj \
+ -scheme "$SCHEME" \
+ -configuration "$CONFIG" \
+ -archivePath "$ARCHIVE_PATH" \
+ -destination "generic/platform=macOS" \
+ DEVELOPMENT_TEAM="$DEVELOPMENT_TEAM" \
+ CODE_SIGN_STYLE=Automatic
+
+EXPORT_OPTS="$ROOT/build/ExportOptions.plist"
+sed "s/\$(DEVELOPMENT_TEAM)/$DEVELOPMENT_TEAM/g" "$ROOT/Scripts/ExportOptions.plist" > "$EXPORT_OPTS"
+
+echo "==> Exporting Developer ID build"
+rm -rf "$EXPORT_PATH"
+xcodebuild -exportArchive \
+ -archivePath "$ARCHIVE_PATH" \
+ -exportPath "$EXPORT_PATH" \
+ -exportOptionsPlist "$EXPORT_OPTS"
+
+echo "==> Verifying code signature"
+codesign --verify --deep --strict "$APP_PATH"
+
+echo "==> Creating zip"
+rm -f "$ZIP_PATH"
+ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
+
+if [[ "${SKIP_NOTARIZE:-}" != "1" ]]; then
+ if [[ -z "${APPLE_ID:-}" || -z "${APPLE_APP_PASSWORD:-}" ]]; then
+ echo "Set APPLE_ID and APPLE_APP_PASSWORD to notarize, or SKIP_NOTARIZE=1." >&2
+ exit 1
+ fi
+ echo "==> Submitting for notarization"
+ xcrun notarytool submit "$ZIP_PATH" \
+ --apple-id "$APPLE_ID" \
+ --password "$APPLE_APP_PASSWORD" \
+ --team-id "$DEVELOPMENT_TEAM" \
+ --wait
+ echo "==> Stapling ticket"
+ xcrun stapler staple "$APP_PATH"
+ rm -f "$ZIP_PATH"
+ ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"
+fi
+
+mkdir -p "$(dirname "$WEBSITE_ZIP")"
+cp "$ZIP_PATH" "$WEBSITE_ZIP"
+echo "==> Done: $ZIP_PATH"
+echo " Website artifact: $WEBSITE_ZIP"
+echo "Next: update Website/public/appcast.xml sparkle:edSignature and deploy."
diff --git a/Scripts/smoke_check.swift b/Scripts/smoke_check.swift
new file mode 100644
index 0000000..a6f66cc
--- /dev/null
+++ b/Scripts/smoke_check.swift
@@ -0,0 +1,66 @@
+#!/usr/bin/env swift
+import Foundation
+
+// Non-GUI smoke checks for Parcel core modules logic (run from repo root after build).
+// Validates identity migration keys, output formats, and bundle structure.
+
+enum Smoke {
+ static func run() {
+ var passed = 0
+ var failed = 0
+
+ func check(_ name: String, _ ok: Bool) {
+ if ok { passed += 1; print("PASS: \(name)") }
+ else { failed += 1; print("FAIL: \(name)") }
+ }
+
+ let appPath = ".derivedData/Build/Products/Debug/Parcel.app"
+ let infoPlist = "\(appPath)/Contents/Info.plist"
+ check("Parcel.app exists", FileManager.default.fileExists(atPath: appPath))
+ check("Info.plist exists", FileManager.default.fileExists(atPath: infoPlist))
+
+ if let plist = NSDictionary(contentsOfFile: infoPlist) {
+ check("Display name is Parcel", plist["CFBundleDisplayName"] as? String == "Parcel")
+ check("Bundle ID is dev.parable.Parcel", plist["CFBundleIdentifier"] as? String == "dev.parable.Parcel")
+ check("Mic usage string present", (plist["NSMicrophoneUsageDescription"] as? String)?.isEmpty == false)
+ check("Sparkle feed URL present", (plist["SUFeedURL"] as? String)?.contains("appcast") == true)
+ check("Menu bar app (LSUIElement)", plist["LSUIElement"] as? Bool == true)
+ } else {
+ check("Info.plist readable", false)
+ }
+
+ let entitlements = "\(appPath)/Contents/embedded.provisionprofile"
+ // ad-hoc builds may not embed profile; check entitlements via codesign
+ let task = Process()
+ task.executableURL = URL(fileURLWithPath: "/usr/bin/codesign")
+ task.arguments = ["-d", "--entitlements", ":-", appPath]
+ let pipe = Pipe()
+ task.standardOutput = pipe
+ try? task.run()
+ task.waitUntilExit()
+ let entData = pipe.fileHandleForReading.readDataToEndOfFile()
+ if let entStr = String(data: entData, encoding: .utf8) {
+ check("Sandbox enabled", entStr.contains("app-sandbox") && entStr.contains(" "))
+ check("User-selected read-write", entStr.contains("user-selected.read-write"))
+ } else {
+ check("Entitlements readable", false)
+ }
+
+ let iconPath = "\(appPath)/Contents/Resources/AppIcon.icns"
+ let assetsIcon = "\(appPath)/Contents/Resources/Assets.car"
+ check("Has AppIcon or asset catalog", FileManager.default.fileExists(atPath: iconPath) || FileManager.default.fileExists(atPath: assetsIcon))
+
+ check("Sparkle framework embedded", FileManager.default.fileExists(atPath: "\(appPath)/Contents/Frameworks/Sparkle.framework"))
+
+ let websiteDist = "Website/dist/index.html"
+ check("Website built", FileManager.default.fileExists(atPath: websiteDist))
+
+ let appcast = "Website/public/appcast.xml"
+ check("Sparkle appcast exists", FileManager.default.fileExists(atPath: appcast))
+
+ print("\n--- \(passed) passed, \(failed) failed ---")
+ if failed > 0 { exit(1) }
+ }
+}
+
+Smoke.run()
diff --git a/Sources/Notable/App/AppDelegate.swift b/Sources/Notable/App/AppDelegate.swift
deleted file mode 100644
index 40d0409..0000000
--- a/Sources/Notable/App/AppDelegate.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-import AppKit
-
-@MainActor
-final class AppDelegate: NSObject, NSApplicationDelegate {
-
- let coordinator = AppCoordinator()
-
- func applicationDidFinishLaunching(_ notification: Notification) {
- // Menu bar utility: no dock icon, lives quietly in the menu bar.
- NSApp.setActivationPolicy(.accessory)
- coordinator.start()
- }
-
- func applicationWillTerminate(_ notification: Notification) {
- coordinator.shutdown()
- }
-}
diff --git a/Sources/Notable/App/NotableApp.swift b/Sources/Notable/App/NotableApp.swift
deleted file mode 100644
index 0e1681d..0000000
--- a/Sources/Notable/App/NotableApp.swift
+++ /dev/null
@@ -1,16 +0,0 @@
-import SwiftUI
-
-@main
-struct NotableApp: App {
- @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
-
- var body: some Scene {
- MenuBarExtra {
- MenuBarContent(coordinator: appDelegate.coordinator)
- } label: {
- // Placeholder menu bar icon β flagged in CLAUDE.md for a custom icon pass.
- Image(systemName: "viewfinder")
- }
- .menuBarExtraStyle(.menu)
- }
-}
diff --git a/Sources/Notable/Preferences/PreferencesView.swift b/Sources/Notable/Preferences/PreferencesView.swift
deleted file mode 100644
index fa28106..0000000
--- a/Sources/Notable/Preferences/PreferencesView.swift
+++ /dev/null
@@ -1,56 +0,0 @@
-import SwiftUI
-
-/// Bare-minimum Preferences for Phase 1: permission status + the current shortcut. Deliberately
-/// no more than the prompt allows (permissions plumbing only).
-struct PreferencesView: View {
- @State private var hasScreenPermission = ScreenRecordingPermission.isGranted
-
- private let poll = Timer.publish(every: 1.5, on: .main, in: .common).autoconnect()
-
- var body: some View {
- Form {
- Section("Permissions") {
- HStack {
- VStack(alignment: .leading, spacing: 2) {
- Text("Screen Recording")
- Text("Required to capture your screen. After granting, quit and reopen Notable.")
- .font(.footnote)
- .foregroundStyle(.secondary)
- }
- Spacer()
- if hasScreenPermission {
- Label("Granted", systemImage: "checkmark.circle.fill")
- .foregroundStyle(.green)
- .labelStyle(.titleAndIcon)
- } else {
- Button("Grantβ¦") {
- ScreenRecordingPermission.request()
- ScreenRecordingPermission.openSystemSettings()
- }
- }
- }
- }
-
- Section("Shortcut") {
- LabeledContent("Capture Region") {
- Text("ββ§2").font(.system(.body, design: .monospaced))
- }
- }
- }
- .formStyle(.grouped)
- .frame(width: 440, height: 260)
- .task {
- await refreshPermissionStatus()
- }
- .onReceive(poll) { _ in
- Task { await refreshPermissionStatus() }
- }
- }
-
- @MainActor
- private func refreshPermissionStatus() async {
- let preflight = ScreenRecordingPermission.isGranted
- let captureAPIWorks = preflight ? true : await ScreenRecordingPermission.canUseCaptureAPI()
- hasScreenPermission = preflight || captureAPIWorks
- }
-}
diff --git a/Sources/Notable/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sources/Notable/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
deleted file mode 100644
index d010a90..0000000
--- a/Sources/Notable/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "images" : [
- { "idiom" : "mac", "scale" : "1x", "size" : "16x16" },
- { "idiom" : "mac", "scale" : "2x", "size" : "16x16" },
- { "idiom" : "mac", "scale" : "1x", "size" : "32x32" },
- { "idiom" : "mac", "scale" : "2x", "size" : "32x32" },
- { "idiom" : "mac", "scale" : "1x", "size" : "128x128" },
- { "idiom" : "mac", "scale" : "2x", "size" : "128x128" },
- { "idiom" : "mac", "scale" : "1x", "size" : "256x256" },
- { "idiom" : "mac", "scale" : "2x", "size" : "256x256" },
- { "idiom" : "mac", "scale" : "1x", "size" : "512x512" },
- { "idiom" : "mac", "scale" : "2x", "size" : "512x512" }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/Sources/Notable/Resources/Notable.entitlements b/Sources/Notable/Resources/Notable.entitlements
deleted file mode 100644
index 9e612f5..0000000
--- a/Sources/Notable/Resources/Notable.entitlements
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
- com.apple.security.app-sandbox
-
-
-
diff --git a/Sources/Notable/App/AppCoordinator.swift b/Sources/Parcel/App/AppCoordinator.swift
similarity index 92%
rename from Sources/Notable/App/AppCoordinator.swift
rename to Sources/Parcel/App/AppCoordinator.swift
index 7b40fb0..f9c7506 100644
--- a/Sources/Notable/App/AppCoordinator.swift
+++ b/Sources/Parcel/App/AppCoordinator.swift
@@ -47,10 +47,20 @@ final class AppCoordinator: ObservableObject {
hotKeys.onHotKey = { [weak self] in
self?.beginRegionCapture()
}
+ NotificationCenter.default.addObserver(
+ forName: .hotKeyPreferencesDidChange, object: nil, queue: .main
+ ) { [weak self] _ in
+ self?.hotKeys.registerDefault()
+ }
recorder.onFailure = { [weak self] error in
self?.isRecording = false
self?.isStartingRecording = false
- self?.presentError(title: "Recording Failed", error: error)
+ if let recorderError = error as? ScreenRecorder.RecorderError,
+ recorderError == .permissionDenied {
+ self?.openPreferences()
+ } else {
+ self?.presentError(title: "Recording Failed", error: error)
+ }
}
hotKeys.registerDefault()
}
@@ -171,6 +181,8 @@ final class AppCoordinator: ObservableObject {
do {
try await recorder.start(to: url)
isRecording = true
+ } catch let error as ScreenRecorder.RecorderError where error == .permissionDenied {
+ openPreferences()
} catch {
presentError(title: "Could Not Start Recording", error: error)
}
@@ -255,6 +267,6 @@ final class AppCoordinator: ObservableObject {
private func recordingFileName() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd 'at' HH.mm.ss"
- return "Notable Recording \(formatter.string(from: Date())).mp4"
+ return "Parcel Recording \(formatter.string(from: Date())).mp4"
}
}
diff --git a/Sources/Parcel/App/AppDelegate.swift b/Sources/Parcel/App/AppDelegate.swift
new file mode 100644
index 0000000..fffaffd
--- /dev/null
+++ b/Sources/Parcel/App/AppDelegate.swift
@@ -0,0 +1,48 @@
+import AppKit
+import Sparkle
+
+@MainActor
+final class AppDelegate: NSObject, NSApplicationDelegate {
+
+ let coordinator = AppCoordinator()
+ private var updaterController: SPUStandardUpdaterController!
+ private lazy var welcome = WelcomeWindowController(
+ onOpenPreferences: { [weak self] in self?.coordinator.openPreferences() },
+ onFinish: { [weak self] in self?.markOnboardingComplete() }
+ )
+
+ func applicationDidFinishLaunching(_ notification: Notification) {
+ NSApp.setActivationPolicy(.accessory)
+ AppIdentity.migrateFromNotableIfNeeded()
+ updaterController = SPUStandardUpdaterController(
+ startingUpdater: true,
+ updaterDelegate: nil,
+ userDriverDelegate: nil
+ )
+ coordinator.start()
+ showOnboardingIfNeeded()
+ }
+
+ func applicationWillTerminate(_ notification: Notification) {
+ coordinator.shutdown()
+ }
+
+ func checkForUpdates() {
+ updaterController.checkForUpdates(nil)
+ }
+
+ // MARK: Private
+
+ private func showOnboardingIfNeeded() {
+ guard !UserDefaults.standard.bool(forKey: OnboardingKeys.completed) else { return }
+ welcome.show()
+ }
+
+ private func markOnboardingComplete() {
+ UserDefaults.standard.set(true, forKey: OnboardingKeys.completed)
+ }
+}
+
+enum OnboardingKeys {
+ static let completed = "dev.parable.onboardingCompleted"
+}
diff --git a/Sources/Parcel/App/ParcelApp.swift b/Sources/Parcel/App/ParcelApp.swift
new file mode 100644
index 0000000..4e76c04
--- /dev/null
+++ b/Sources/Parcel/App/ParcelApp.swift
@@ -0,0 +1,19 @@
+import SwiftUI
+
+@main
+struct ParcelApp: App {
+ @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
+
+ var body: some Scene {
+ MenuBarExtra {
+ MenuBarContent(
+ coordinator: appDelegate.coordinator,
+ onCheckForUpdates: { appDelegate.checkForUpdates() }
+ )
+ } label: {
+ Image("MenuBarIcon")
+ .renderingMode(.template)
+ }
+ .menuBarExtraStyle(.menu)
+ }
+}
diff --git a/Sources/Notable/Capture/CaptureController.swift b/Sources/Parcel/Capture/CaptureController.swift
similarity index 86%
rename from Sources/Notable/Capture/CaptureController.swift
rename to Sources/Parcel/Capture/CaptureController.swift
index 2736b2e..b3772d9 100644
--- a/Sources/Notable/Capture/CaptureController.swift
+++ b/Sources/Parcel/Capture/CaptureController.swift
@@ -52,8 +52,9 @@ final class CaptureController {
let frozen = try await engine.freezeScreens()
presentOverlay(frozen)
} catch {
- NSLog("Notable: capture failed β \(error)")
- if !ScreenRecordingPermission.isGranted {
+ NSLog("Parcel: capture failed β \(error)")
+ let hasAccess = await ScreenRecordingPermission.hasEffectiveAccess()
+ if !hasAccess {
onNeedsPermission?()
} else {
onFailure?("Could not freeze the screen for Capture. \(error.localizedDescription)")
@@ -73,8 +74,9 @@ final class CaptureController {
onFailure?("Could not stitch all displays into one Capture.")
}
} catch {
- NSLog("Notable: multi-display capture failed β \(error)")
- if !ScreenRecordingPermission.isGranted {
+ NSLog("Parcel: multi-display capture failed β \(error)")
+ let hasAccess = await ScreenRecordingPermission.hasEffectiveAccess()
+ if !hasAccess {
onNeedsPermission?()
} else {
onFailure?("Could not capture all displays. \(error.localizedDescription)")
@@ -86,7 +88,10 @@ final class CaptureController {
private func presentOverlay(_ frozen: [FrozenScreen]) {
let controller = OverlayController(screens: frozen)
controller.onSelection = { [weak self] result in
- self?.handle(result)
+ self?.handle(result, scroll: false)
+ }
+ controller.onScrollSelection = { [weak self] result in
+ self?.handle(result, scroll: true)
}
controller.onCancel = { [weak self] in
self?.dismissOverlay()
@@ -96,10 +101,10 @@ final class CaptureController {
controller.present()
}
- private func handle(_ result: SelectionResult) {
+ private func handle(_ result: SelectionResult, scroll: Bool) {
dismissOverlay()
if let capture = engine.makeCapture(from: result) {
- if isScrollSelection {
+ if scroll || isScrollSelection {
onScrollSelection?(result, capture)
} else {
onCaptureComplete?(capture)
diff --git a/Sources/Notable/Capture/CaptureEngine.swift b/Sources/Parcel/Capture/CaptureEngine.swift
similarity index 99%
rename from Sources/Notable/Capture/CaptureEngine.swift
rename to Sources/Parcel/Capture/CaptureEngine.swift
index 1956870..5ce9d7c 100644
--- a/Sources/Notable/Capture/CaptureEngine.swift
+++ b/Sources/Parcel/Capture/CaptureEngine.swift
@@ -166,7 +166,7 @@ private final class SingleFrameStreamCapturer: NSObject, SCStreamOutput, @unchec
private var stream: SCStream?
private let lock = NSLock()
private var finished = false
- private let sampleQueue = DispatchQueue(label: "io.notable.capture.singleframe")
+ private let sampleQueue = DispatchQueue(label: "dev.parable.capture.singleframe")
private let ciContext = CIContext()
func capture(filter: SCContentFilter, config: SCStreamConfiguration) async throws -> CGImage {
diff --git a/Sources/Notable/Capture/CaptureModels.swift b/Sources/Parcel/Capture/CaptureModels.swift
similarity index 100%
rename from Sources/Notable/Capture/CaptureModels.swift
rename to Sources/Parcel/Capture/CaptureModels.swift
diff --git a/Sources/Notable/Capture/OverlayController.swift b/Sources/Parcel/Capture/OverlayController.swift
similarity index 73%
rename from Sources/Notable/Capture/OverlayController.swift
rename to Sources/Parcel/Capture/OverlayController.swift
index 3ccf640..ff3d65d 100644
--- a/Sources/Notable/Capture/OverlayController.swift
+++ b/Sources/Parcel/Capture/OverlayController.swift
@@ -7,6 +7,7 @@ import SwiftUI
final class OverlayController {
var onSelection: ((SelectionResult) -> Void)?
+ var onScrollSelection: ((SelectionResult) -> Void)?
var onCancel: (() -> Void)?
private let screens: [FrozenScreen]
@@ -23,9 +24,15 @@ final class OverlayController {
for frozen in screens {
let window = OverlayWindow(screen: frozen.screen)
- let root = SelectionOverlayView(frozen: frozen) { [weak self] rect in
- self?.commit(SelectionResult(screen: frozen, rectInPoints: rect))
- }
+ let root = SelectionOverlayView(
+ frozen: frozen,
+ onCommit: { [weak self] rect in
+ self?.commit(SelectionResult(screen: frozen, rectInPoints: rect), scroll: false)
+ },
+ onScrollCommit: onScrollSelection == nil ? nil : { [weak self] rect in
+ self?.commit(SelectionResult(screen: frozen, rectInPoints: rect), scroll: true)
+ }
+ )
let hosting = NSHostingView(rootView: root)
hosting.frame = window.contentLayoutRect
hosting.autoresizingMask = [.width, .height]
@@ -52,10 +59,14 @@ final class OverlayController {
// MARK: Private
- private func commit(_ result: SelectionResult) {
+ private func commit(_ result: SelectionResult, scroll: Bool) {
guard !finished else { return }
finished = true
- onSelection?(result)
+ if scroll {
+ onScrollSelection?(result)
+ } else {
+ onSelection?(result)
+ }
}
private func cancel() {
diff --git a/Sources/Notable/Capture/OverlayWindow.swift b/Sources/Parcel/Capture/OverlayWindow.swift
similarity index 100%
rename from Sources/Notable/Capture/OverlayWindow.swift
rename to Sources/Parcel/Capture/OverlayWindow.swift
diff --git a/Sources/Notable/Capture/ScrollCapture.swift b/Sources/Parcel/Capture/ScrollCapture.swift
similarity index 100%
rename from Sources/Notable/Capture/ScrollCapture.swift
rename to Sources/Parcel/Capture/ScrollCapture.swift
diff --git a/Sources/Notable/Capture/SelectionOverlayView.swift b/Sources/Parcel/Capture/SelectionOverlayView.swift
similarity index 80%
rename from Sources/Notable/Capture/SelectionOverlayView.swift
rename to Sources/Parcel/Capture/SelectionOverlayView.swift
index b96a32e..d85f232 100644
--- a/Sources/Notable/Capture/SelectionOverlayView.swift
+++ b/Sources/Parcel/Capture/SelectionOverlayView.swift
@@ -8,12 +8,15 @@ struct SelectionOverlayView: View {
let frozen: FrozenScreen
/// Commit a Selection rect (local top-left points) for this display.
let onCommit: (CGRect) -> Void
+ /// When set, a Scroll Capture button appears; commits route here instead of `onCommit`.
+ var onScrollCommit: ((CGRect) -> Void)? = nil
@State private var dragOrigin: CGPoint?
@State private var liveRect: CGRect?
@State private var hoverWindow: SnapWindow?
@State private var isDragging = false
@State private var aspectPreset: SelectionAspectPreset = .free
+ @State private var isScrollMode = false
/// The rect currently emphasized: an in-progress drag, else the hovered window.
private var highlightRect: CGRect? {
@@ -38,6 +41,18 @@ struct SelectionOverlayView: View {
sizeReadout(for: rect)
}
+ VStack {
+ Spacer()
+ HStack {
+ overlayHints
+ Spacer()
+ if onScrollCommit != nil {
+ scrollCaptureButton
+ }
+ }
+ .padding(14)
+ }
+
aspectMenu
.padding(14)
}
@@ -76,15 +91,23 @@ struct SelectionOverlayView: View {
if travelled < 4 {
// A click β snap to the window under the cursor, if any.
if let win = frozen.window(at: value.location) {
- onCommit(win.frameInScreen.integral)
+ commitSelection(win.frameInScreen.integral)
}
} else {
let rect = selectionRect(start: value.startLocation, current: value.location).integral
- if rect.width >= 1, rect.height >= 1 { onCommit(rect) }
+ if rect.width >= 1, rect.height >= 1 { commitSelection(rect) }
}
}
}
+ private func commitSelection(_ rect: CGRect) {
+ if isScrollMode, let onScrollCommit {
+ onScrollCommit(rect)
+ } else {
+ onCommit(rect)
+ }
+ }
+
/// Snaps the actively dragged corner to display and detected-window boundaries, then applies
/// an optional aspect preset while retaining the original drag direction.
private func selectionRect(start: CGPoint, current: CGPoint) -> CGRect {
@@ -153,6 +176,31 @@ struct SelectionOverlayView: View {
.allowsHitTesting(false)
}
+ private var overlayHints: some View {
+ VStack(alignment: .leading, spacing: 4) {
+ Text(isScrollMode ? "Scroll Capture β drag a tall region" : "Drag to select Β· Click window to snap")
+ .font(.system(size: 11, weight: .medium))
+ Text("Esc to cancel")
+ .font(.system(size: 10))
+ .foregroundStyle(.secondary)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 8)
+ .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 8))
+ }
+
+ private var scrollCaptureButton: some View {
+ Button {
+ isScrollMode.toggle()
+ } label: {
+ Label(isScrollMode ? "Scroll mode on" : "Scroll Capture", systemImage: "arrow.up.and.down.text.horizontal")
+ .font(.system(size: 11, weight: .medium))
+ }
+ .buttonStyle(.borderedProminent)
+ .tint(isScrollMode ? .orange : .accentColor)
+ .help("Select a region, then scroll the source to stitch frames")
+ }
+
private var aspectMenu: some View {
Menu {
ForEach(SelectionAspectPreset.allCases) { preset in
diff --git a/Sources/Notable/Editor/Adjustments.swift b/Sources/Parcel/Editor/Adjustments.swift
similarity index 100%
rename from Sources/Notable/Editor/Adjustments.swift
rename to Sources/Parcel/Editor/Adjustments.swift
diff --git a/Sources/Notable/Editor/AdjustmentsPanel.swift b/Sources/Parcel/Editor/AdjustmentsPanel.swift
similarity index 100%
rename from Sources/Notable/Editor/AdjustmentsPanel.swift
rename to Sources/Parcel/Editor/AdjustmentsPanel.swift
diff --git a/Sources/Notable/Editor/AnnotatedCanvas.swift b/Sources/Parcel/Editor/AnnotatedCanvas.swift
similarity index 92%
rename from Sources/Notable/Editor/AnnotatedCanvas.swift
rename to Sources/Parcel/Editor/AnnotatedCanvas.swift
index 61de619..2d83ceb 100644
--- a/Sources/Notable/Editor/AnnotatedCanvas.swift
+++ b/Sources/Parcel/Editor/AnnotatedCanvas.swift
@@ -51,8 +51,24 @@ struct AnnotatedCanvas: View {
private func draw(_ annotation: Annotation, in context: inout GraphicsContext) {
let style = annotation.style
let color = GraphicsContext.Shading.color(style.color.color)
+ let center = annotation.kind.boundingBox.center
+ let rotation = annotation.rotation
- switch annotation.kind {
+ context.drawLayer { layer in
+ if rotation != 0 {
+ layer.translateBy(x: center.x, y: center.y)
+ layer.rotate(by: .radians(rotation))
+ layer.translateBy(x: -center.x, y: -center.y)
+ }
+ drawKind(annotation.kind, style: style, color: color, in: &layer)
+ }
+ }
+
+ private func drawKind(
+ _ kind: AnnotationKind, style: AnnotationStyle, color: GraphicsContext.Shading,
+ in context: inout GraphicsContext
+ ) {
+ switch kind {
case let .arrow(start, end):
drawArrow(from: start, to: end, style: style, in: &context)
@@ -63,6 +79,13 @@ struct AnnotatedCanvas: View {
style: StrokeStyle(lineWidth: style.lineWidth, lineJoin: .round)
)
+ case let .ellipse(rect):
+ context.stroke(
+ Path(ellipseIn: rect.standardized),
+ with: color,
+ style: StrokeStyle(lineWidth: style.lineWidth, lineJoin: .round)
+ )
+
case let .pencil(points):
var path = Path()
path.addLines(points)
@@ -173,7 +196,7 @@ struct AnnotatedCanvas: View {
}
}
- // MARK: Censor (3 modes)
+ // MARK: Censor (4 modes)
private func drawCensor(rect: CGRect, style: AnnotationStyle, in context: inout GraphicsContext) {
guard rect.width > 0, rect.height > 0 else { return }
@@ -184,6 +207,13 @@ struct AnnotatedCanvas: View {
drawImageClipped(pixelated, to: rect, in: &context)
case .solid:
context.fill(Path(rect), with: .color(style.color.color))
+ case .erase:
+ if let cg = base.cgImage(forProposedRect: nil, context: nil, hints: nil),
+ let fill = CensorEraseSampler.averageBorderColor(in: rect, image: cg) {
+ context.fill(Path(rect), with: .color(fill.color))
+ } else {
+ context.fill(Path(rect), with: .color(style.color.color))
+ }
}
}
diff --git a/Sources/Notable/Editor/Annotation.swift b/Sources/Parcel/Editor/Annotation.swift
similarity index 89%
rename from Sources/Notable/Editor/Annotation.swift
rename to Sources/Parcel/Editor/Annotation.swift
index 80dd951..5e1dfc0 100644
--- a/Sources/Notable/Editor/Annotation.swift
+++ b/Sources/Parcel/Editor/Annotation.swift
@@ -5,7 +5,7 @@ import SwiftUI
/// The active annotation mode. `.select` edits existing Annotations; drawing Tools create new
/// ones; utility Tools (Loupe, Eyedropper) are ephemeral overlays that never create Annotations.
enum Tool: String, CaseIterable, Identifiable {
- case select, arrow, rectangle, text, pencil, censor
+ case select, arrow, rectangle, ellipse, text, pencil, censor
case number, stamp, highlighter, measure, spotlight
case loupe, eyedropper
@@ -20,6 +20,7 @@ enum Tool: String, CaseIterable, Identifiable {
case .select: return "cursorarrow"
case .arrow: return "arrow.up.right"
case .rectangle: return "rectangle"
+ case .ellipse: return "oval"
case .text: return "textformat"
case .pencil: return "scribble.variable"
case .censor: return "eye.slash"
@@ -38,6 +39,7 @@ enum Tool: String, CaseIterable, Identifiable {
case .select: return "Select"
case .arrow: return "Arrow"
case .rectangle: return "Rectangle"
+ case .ellipse: return "Ellipse"
case .text: return "Text"
case .pencil: return "Pencil"
case .censor: return "Censor"
@@ -90,6 +92,7 @@ enum CensorMode: String, CaseIterable, Identifiable, Equatable, Codable {
case blur // gaussian blur of the (adjusted) Capture
case pixelate // mosaic of the (adjusted) Capture
case solid // opaque fill in the Annotation's color
+ case erase // local fill sampled from surrounding Capture pixels
var id: String { rawValue }
@@ -98,6 +101,7 @@ enum CensorMode: String, CaseIterable, Identifiable, Equatable, Codable {
case .blur: return "Blur"
case .pixelate: return "Pixelate"
case .solid: return "Solid"
+ case .erase: return "Erase"
}
}
@@ -106,6 +110,7 @@ enum CensorMode: String, CaseIterable, Identifiable, Equatable, Codable {
case .blur: return "drop.fill"
case .pixelate: return "square.grid.3x3.fill"
case .solid: return "square.fill"
+ case .erase: return "eraser.fill"
}
}
}
@@ -171,6 +176,7 @@ struct AnnotationStyle: Equatable, Codable {
enum Handle: Equatable {
case topLeft, topRight, bottomLeft, bottomRight // rect-like
case start, end // arrow/measure endpoints
+ case rotate // rotation affordance above selection
}
/// The geometry + payload of one Annotation, in **Capture point coordinates** (resolution
@@ -178,6 +184,7 @@ enum Handle: Equatable {
enum AnnotationKind: Equatable, Codable {
case arrow(start: CGPoint, end: CGPoint)
case rectangle(rect: CGRect)
+ case ellipse(rect: CGRect)
case censor(rect: CGRect)
case text(rect: CGRect, string: String)
case pencil(points: [CGPoint])
@@ -198,11 +205,34 @@ struct Annotation: Identifiable, Equatable, Codable {
let id: UUID
var kind: AnnotationKind
var style: AnnotationStyle
+ /// Radians, applied around the kind's bounding-box center when rendering.
+ var rotation: CGFloat
- init(id: UUID = UUID(), kind: AnnotationKind, style: AnnotationStyle) {
+ init(id: UUID = UUID(), kind: AnnotationKind, style: AnnotationStyle, rotation: CGFloat = 0) {
self.id = id
self.kind = kind
self.style = style
+ self.rotation = rotation
+ }
+
+ private enum CodingKeys: String, CodingKey {
+ case id, kind, style, rotation
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ id = try container.decode(UUID.self, forKey: .id)
+ kind = try container.decode(AnnotationKind.self, forKey: .kind)
+ style = try container.decode(AnnotationStyle.self, forKey: .style)
+ rotation = try container.decodeIfPresent(CGFloat.self, forKey: .rotation) ?? 0
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(id, forKey: .id)
+ try container.encode(kind, forKey: .kind)
+ try container.encode(style, forKey: .style)
+ if rotation != 0 { try container.encode(rotation, forKey: .rotation) }
}
/// Style-aware hit test (arrow shafts follow their ArrowStyle; highlighter uses its true width).
@@ -217,7 +247,7 @@ extension AnnotationKind {
}
private enum Kind: String, Codable {
- case arrow, rectangle, censor, text, pencil, number, stamp, highlight, measure, spotlight
+ case arrow, rectangle, ellipse, censor, text, pencil, number, stamp, highlight, measure, spotlight
}
init(from decoder: Decoder) throws {
@@ -230,6 +260,8 @@ extension AnnotationKind {
)
case .rectangle:
self = .rectangle(rect: try container.decode(CGRect.self, forKey: .rect))
+ case .ellipse:
+ self = .ellipse(rect: try container.decode(CGRect.self, forKey: .rect))
case .censor:
self = .censor(rect: try container.decode(CGRect.self, forKey: .rect))
case .text:
@@ -276,6 +308,9 @@ extension AnnotationKind {
case let .rectangle(rect):
try container.encode(Kind.rectangle, forKey: .type)
try container.encode(rect, forKey: .rect)
+ case let .ellipse(rect):
+ try container.encode(Kind.ellipse, forKey: .type)
+ try container.encode(rect, forKey: .rect)
case let .censor(rect):
try container.encode(Kind.censor, forKey: .type)
try container.encode(rect, forKey: .rect)
@@ -356,7 +391,7 @@ extension AnnotationKind {
switch self {
case let .arrow(start, end), let .measure(start, end, _):
return CGRect(corner: start, corner: end)
- case let .rectangle(rect), let .censor(rect), let .text(rect, _),
+ case let .rectangle(rect), let .ellipse(rect), let .censor(rect), let .text(rect, _),
let .stamp(rect, _), let .spotlight(rect, _):
return rect.standardized
case let .number(center, radius, _):
@@ -405,7 +440,7 @@ extension AnnotationKind {
case let .number(center, radius, _):
return point.distance(to: center) <= radius + slop
- case .rectangle, .censor, .text, .stamp, .spotlight:
+ case .rectangle, .ellipse, .censor, .text, .stamp, .spotlight:
return boundingBox.insetBy(dx: -slop, dy: -slop).contains(point)
}
}
@@ -425,13 +460,15 @@ extension AnnotationKind {
switch self {
case let .arrow(start, end), let .measure(start, end, _):
return [(.start, start), (.end, end)]
- case .rectangle, .censor, .text, .stamp, .spotlight, .number:
+ case .rectangle, .ellipse, .censor, .text, .stamp, .spotlight, .number:
let r = boundingBox
+ let rotatePoint = CGPoint(x: r.midX, y: r.minY - 24)
return [
(.topLeft, CGPoint(x: r.minX, y: r.minY)),
(.topRight, CGPoint(x: r.maxX, y: r.minY)),
(.bottomLeft, CGPoint(x: r.minX, y: r.maxY)),
(.bottomRight, CGPoint(x: r.maxX, y: r.maxY)),
+ (.rotate, rotatePoint),
]
case .pencil, .highlight:
return []
@@ -444,6 +481,8 @@ extension AnnotationKind {
return .arrow(start: start.offset(dx: dx, dy: dy), end: end.offset(dx: dx, dy: dy))
case let .rectangle(rect):
return .rectangle(rect: rect.offsetBy(dx: dx, dy: dy))
+ case let .ellipse(rect):
+ return .ellipse(rect: rect.offsetBy(dx: dx, dy: dy))
case let .censor(rect):
return .censor(rect: rect.offsetBy(dx: dx, dy: dy))
case let .text(rect, string):
@@ -479,6 +518,8 @@ extension AnnotationKind {
}
case let .rectangle(rect):
return .rectangle(rect: Self.resize(rect, handle: handle, to: point))
+ case let .ellipse(rect):
+ return .ellipse(rect: Self.resize(rect, handle: handle, to: point))
case let .censor(rect):
return .censor(rect: Self.resize(rect, handle: handle, to: point))
case let .text(rect, string):
diff --git a/Sources/Notable/Editor/BeautifyCanvas.swift b/Sources/Parcel/Editor/BeautifyCanvas.swift
similarity index 100%
rename from Sources/Notable/Editor/BeautifyCanvas.swift
rename to Sources/Parcel/Editor/BeautifyCanvas.swift
diff --git a/Sources/Notable/Editor/BeautifyModels.swift b/Sources/Parcel/Editor/BeautifyModels.swift
similarity index 100%
rename from Sources/Notable/Editor/BeautifyModels.swift
rename to Sources/Parcel/Editor/BeautifyModels.swift
diff --git a/Sources/Notable/Editor/BeautifyPanel.swift b/Sources/Parcel/Editor/BeautifyPanel.swift
similarity index 100%
rename from Sources/Notable/Editor/BeautifyPanel.swift
rename to Sources/Parcel/Editor/BeautifyPanel.swift
diff --git a/Sources/Notable/Editor/CaptureAdjuster.swift b/Sources/Parcel/Editor/CaptureAdjuster.swift
similarity index 100%
rename from Sources/Notable/Editor/CaptureAdjuster.swift
rename to Sources/Parcel/Editor/CaptureAdjuster.swift
diff --git a/Sources/Notable/Editor/CapturePixelSampler.swift b/Sources/Parcel/Editor/CapturePixelSampler.swift
similarity index 100%
rename from Sources/Notable/Editor/CapturePixelSampler.swift
rename to Sources/Parcel/Editor/CapturePixelSampler.swift
diff --git a/Sources/Parcel/Editor/CensorEraseSampler.swift b/Sources/Parcel/Editor/CensorEraseSampler.swift
new file mode 100644
index 0000000..8243b65
--- /dev/null
+++ b/Sources/Parcel/Editor/CensorEraseSampler.swift
@@ -0,0 +1,56 @@
+import AppKit
+import CoreGraphics
+
+/// Samples surrounding Capture pixels to fill erase-mode Censors locally.
+enum CensorEraseSampler {
+ static func averageBorderColor(in rect: CGRect, image: CGImage) -> RGBAColor? {
+ let r = rect.standardized.integral
+ guard r.width > 0, r.height > 0 else { return nil }
+
+ let width = image.width
+ let height = image.height
+ guard let data = image.dataProvider?.data,
+ let ptr = CFDataGetBytePtr(data) else { return nil }
+
+ let bytesPerPixel = image.bitsPerPixel / 8
+ let bytesPerRow = image.bytesPerRow
+ guard bytesPerPixel >= 4 else { return nil }
+
+ var sumR = 0.0, sumG = 0.0, sumB = 0.0
+ var count = 0
+
+ func sample(x: Int, y: Int) {
+ guard x >= 0, y >= 0, x < width, y < height else { return }
+ let offset = y * bytesPerRow + x * bytesPerPixel
+ let r8 = Double(ptr[offset])
+ let g8 = Double(ptr[offset + 1])
+ let b8 = Double(ptr[offset + 2])
+ sumR += r8 / 255
+ sumG += g8 / 255
+ sumB += b8 / 255
+ count += 1
+ }
+
+ let minX = max(0, Int(r.minX))
+ let maxX = min(width - 1, Int(r.maxX))
+ let minY = max(0, Int(r.minY))
+ let maxY = min(height - 1, Int(r.maxY))
+ guard minX <= maxX, minY <= maxY else { return nil }
+
+ for x in minX...maxX {
+ sample(x: x, y: minY)
+ if maxY != minY { sample(x: x, y: maxY) }
+ }
+ for y in minY...maxY {
+ sample(x: minX, y: y)
+ if maxX != minX { sample(x: maxX, y: y) }
+ }
+
+ guard count > 0 else { return nil }
+ return RGBAColor(
+ red: sumR / Double(count),
+ green: sumG / Double(count),
+ blue: sumB / Double(count)
+ )
+ }
+}
diff --git a/Sources/Notable/Editor/EditorCanvasView.swift b/Sources/Parcel/Editor/EditorCanvasView.swift
similarity index 93%
rename from Sources/Notable/Editor/EditorCanvasView.swift
rename to Sources/Parcel/Editor/EditorCanvasView.swift
index 3bd94ce..e2d49a8 100644
--- a/Sources/Notable/Editor/EditorCanvasView.swift
+++ b/Sources/Parcel/Editor/EditorCanvasView.swift
@@ -15,6 +15,7 @@ struct EditorCanvasView: View {
@State private var op: DragOp = .none
@State private var lastClick: (time: Date, point: CGPoint)?
@State private var hoverLocation: CGPoint? // canvas-local view points, same space as the drag
+ @State private var rotationDragStartAngle: CGFloat = 0
@FocusState private var textFocused: Bool
private enum DragOp {
@@ -23,6 +24,7 @@ struct EditorCanvasView: View {
case creating
case moving(before: [Annotation], original: AnnotationKind)
case resizing(before: [Annotation], handle: Handle, original: AnnotationKind)
+ case rotating(before: [Annotation], center: CGPoint, originalRotation: CGFloat)
}
var body: some View {
@@ -223,6 +225,9 @@ struct EditorCanvasView: View {
model.updateSelected(kind: original.translated(dx: current.x - start.x, dy: current.y - start.y))
case let .resizing(_, handle, original):
model.updateSelected(kind: original.resized(handle: handle, to: current))
+ case let .rotating(_, center, originalRotation):
+ let currentAngle = atan2(current.y - center.y, current.x - center.x)
+ model.updateSelectedRotation(originalRotation + (currentAngle - rotationDragStartAngle))
case .none, .empty:
break
}
@@ -240,6 +245,8 @@ struct EditorCanvasView: View {
model.commitInteractive(before: before)
case let .resizing(before, _, _):
model.commitInteractive(before: before)
+ case let .rotating(before, _, _):
+ model.commitInteractive(before: before)
case .none, .empty:
break
}
@@ -272,7 +279,13 @@ struct EditorCanvasView: View {
model.flushStyleTransaction()
if let selected = model.selectedAnnotation, let handle = handle(at: start, of: selected, scale: scale) {
- op = .resizing(before: model.annotations, handle: handle, original: selected.kind)
+ if handle == .rotate {
+ let center = selected.kind.boundingBox.center
+ rotationDragStartAngle = atan2(start.y - center.y, start.x - center.x)
+ op = .rotating(before: model.annotations, center: center, originalRotation: selected.rotation)
+ } else {
+ op = .resizing(before: model.annotations, handle: handle, original: selected.kind)
+ }
return
}
if let hit = topmost(at: start, scale: scale) {
@@ -294,6 +307,8 @@ struct EditorCanvasView: View {
kind = .arrow(start: point, end: point)
case .rectangle:
kind = .rectangle(rect: CGRect(origin: point, size: .zero))
+ case .ellipse:
+ kind = .ellipse(rect: CGRect(origin: point, size: .zero))
case .censor:
kind = .censor(rect: CGRect(origin: point, size: .zero))
case .pencil:
@@ -323,6 +338,8 @@ struct EditorCanvasView: View {
draft.kind = .arrow(start: start, end: current)
case .rectangle:
draft.kind = .rectangle(rect: CGRect(corner: start, corner: current))
+ case .ellipse:
+ draft.kind = .ellipse(rect: CGRect(corner: start, corner: current))
case .censor:
draft.kind = .censor(rect: CGRect(corner: start, corner: current))
case let .pencil(points):
@@ -362,7 +379,7 @@ struct EditorCanvasView: View {
if case let .pencil(points) = draft.kind, points.count >= 2 { model.add(draft) }
case .arrow:
if case let .arrow(s, e) = draft.kind, s.distance(to: e) >= 4 { model.add(draft) }
- case .rectangle, .censor:
+ case .rectangle, .ellipse, .censor:
let box = draft.kind.boundingBox
if box.width >= 4, box.height >= 4 { model.add(draft) }
case .number:
diff --git a/Sources/Notable/Editor/EditorModel.swift b/Sources/Parcel/Editor/EditorModel.swift
similarity index 85%
rename from Sources/Notable/Editor/EditorModel.swift
rename to Sources/Parcel/Editor/EditorModel.swift
index 3212f5b..2d0d87a 100644
--- a/Sources/Notable/Editor/EditorModel.swift
+++ b/Sources/Parcel/Editor/EditorModel.swift
@@ -56,7 +56,7 @@ final class EditorModel: ObservableObject {
/// The Adjustments the published images currently reflect (display can lag `adjustments`
/// by the render debounce; export syncs the two).
private var displayedAdjustments: Adjustments = .neutral
- private let renderQueue = DispatchQueue(label: "io.notable.adjustments", qos: .userInitiated)
+ private let renderQueue = DispatchQueue(label: "dev.parable.adjustments", qos: .userInitiated)
private var renderWorkItem: DispatchWorkItem?
// MARK: Beautify
@@ -74,6 +74,13 @@ final class EditorModel: ObservableObject {
@Published private(set) var visionAnalysis = VisionAnalysis()
@Published private(set) var isAnalyzingVision = false
+ @Published private(set) var isUploading = false
+ @Published var statusMessage: String?
+
+ // MARK: Translation (on-device, macOS 15+)
+
+ @Published private(set) var translatedText: String?
+ @Published private(set) var isTranslating = false
// MARK: Utilities (Loupe / Eyedropper) β never touch annotations or undo
@@ -190,6 +197,38 @@ final class EditorModel: ObservableObject {
editingTextID = nil
}
+ func updateSelectedRotation(_ rotation: CGFloat) {
+ guard let id = selectedID, let index = annotations.firstIndex(where: { $0.id == id }) else { return }
+ annotations[index].rotation = rotation
+ }
+
+ func bringForward() { moveSelectedLayer(by: 1) }
+ func sendBackward() { moveSelectedLayer(by: -1) }
+ func bringToFront() {
+ guard let id = selectedID, let index = annotations.firstIndex(where: { $0.id == id }) else { return }
+ guard index < annotations.count - 1 else { return }
+ pushUndo()
+ let item = annotations.remove(at: index)
+ annotations.append(item)
+ }
+
+ func sendToBack() {
+ guard let id = selectedID, let index = annotations.firstIndex(where: { $0.id == id }) else { return }
+ guard index > 0 else { return }
+ pushUndo()
+ let item = annotations.remove(at: index)
+ annotations.insert(item, at: 0)
+ }
+
+ private func moveSelectedLayer(by offset: Int) {
+ guard let id = selectedID, let index = annotations.firstIndex(where: { $0.id == id }) else { return }
+ let target = index + offset
+ guard target >= 0, target < annotations.count else { return }
+ pushUndo()
+ annotations.swapAt(index, target)
+ selectedID = id
+ }
+
/// Live update during a move/resize drag (no undo entry until `commitInteractive`).
func updateSelected(kind: AnnotationKind) {
guard let id = selectedID, let index = annotations.firstIndex(where: { $0.id == id }) else { return }
@@ -472,6 +511,30 @@ final class EditorModel: ObservableObject {
NSPasteboard.general.setString(text, forType: .string)
}
+ func translateRecognizedText() {
+ let text = visionAnalysis.recognizedText
+ guard !text.isEmpty else { return }
+ isTranslating = true
+ translatedText = nil
+ Task { [weak self] in
+ let result = await TranslationService.translate(text)
+ guard let self else { return }
+ translatedText = result
+ isTranslating = false
+ if let result, !result.isEmpty {
+ statusMessage = "Translation ready β copy from Vision panel."
+ } else if !TranslationService.isAvailable {
+ statusMessage = "On-device translation requires macOS 15 or later."
+ }
+ }
+ }
+
+ func copyTranslatedText() {
+ guard let translatedText, !translatedText.isEmpty else { return }
+ NSPasteboard.general.clearContents()
+ NSPasteboard.general.setString(translatedText, forType: .string)
+ }
+
func copyQRCode(_ code: VisionQRCode) {
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(code.payload, forType: .string)
@@ -528,6 +591,38 @@ final class EditorModel: ObservableObject {
}
}
+ func uploadCapture() {
+ endEditingText()
+ guard UploadPreferences.isConfigured else {
+ statusMessage = UploadError.notConfigured.errorDescription
+ return
+ }
+ guard let cgImage = renderedCGImage() else {
+ statusMessage = UploadError.encodeFailed.errorDescription
+ return
+ }
+ let rep = NSBitmapImageRep(cgImage: cgImage)
+ guard let data = rep.representation(using: .png, properties: [:]) else {
+ statusMessage = UploadError.encodeFailed.errorDescription
+ return
+ }
+
+ isUploading = true
+ statusMessage = "Uploadingβ¦"
+ let fileName = Self.defaultFileName(format: .png)
+ Task { [weak self] in
+ do {
+ let url = try await UploadService.uploadPNG(data: data, fileName: fileName)
+ NSPasteboard.general.clearContents()
+ NSPasteboard.general.setString(url.absoluteString, forType: .string)
+ self?.statusMessage = "Uploaded β link copied to clipboard."
+ } catch {
+ self?.statusMessage = error.localizedDescription
+ }
+ self?.isUploading = false
+ }
+ }
+
/// Synchronously renders the adjusted base + whichever censor sources are actually in use
/// for the CURRENT adjustments, so export matches the settled adjustment values β never a
/// mid-debounce frame. If the display is still lagging (debounce pending), it is synced to
@@ -593,7 +688,7 @@ final class EditorModel: ObservableObject {
private func writeImage(to url: URL) {
guard let cgImage = renderedCGImage() else {
- NSLog("Notable: failed to render Capture for save")
+ NSLog("Parcel: failed to render Capture for save")
return
}
let data: Data?
@@ -602,7 +697,7 @@ final class EditorModel: ObservableObject {
guard let destination = CGImageDestinationCreateWithData(
destinationData, outputFormat.contentType.identifier as CFString, 1, nil
) else {
- NSLog("Notable: failed to create HEIC destination")
+ NSLog("Parcel: failed to create HEIC destination")
return
}
CGImageDestinationAddImage(destination, cgImage, nil)
@@ -617,13 +712,13 @@ final class EditorModel: ObservableObject {
data = nil
}
guard let data else {
- NSLog("Notable: failed to encode \(outputFormat.label)")
+ NSLog("Parcel: failed to encode \(outputFormat.label)")
return
}
do {
try data.write(to: url)
} catch {
- NSLog("Notable: failed to write Capture β \(error)")
+ NSLog("Parcel: failed to write Capture β \(error)")
}
}
@@ -646,7 +741,7 @@ final class EditorModel: ObservableObject {
// MARK: Save location memory
- private static let lastSaveDirectoryKey = "io.notable.lastSaveDirectory"
+ private static let lastSaveDirectoryKey = "\(AppIdentity.defaultsPrefix).lastSaveDirectory"
private static var lastSaveDirectory: URL? {
get {
@@ -659,6 +754,6 @@ final class EditorModel: ObservableObject {
private static func defaultFileName(format: OutputFormat) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd 'at' HH.mm.ss"
- return "Notable \(formatter.string(from: Date())).\(format.fileExtension)"
+ return "Parcel \(formatter.string(from: Date())).\(format.fileExtension)"
}
}
diff --git a/Sources/Notable/Editor/EditorView.swift b/Sources/Parcel/Editor/EditorView.swift
similarity index 76%
rename from Sources/Notable/Editor/EditorView.swift
rename to Sources/Parcel/Editor/EditorView.swift
index d683a2e..a45daad 100644
--- a/Sources/Notable/Editor/EditorView.swift
+++ b/Sources/Parcel/Editor/EditorView.swift
@@ -18,18 +18,27 @@ struct EditorView: View {
Divider()
EditorCanvasView(model: model)
.background(Color(nsColor: .underPageBackgroundColor))
+ statusStrip
}
.frame(minWidth: 720, minHeight: 420)
.background(Color(nsColor: .windowBackgroundColor))
}
+ private static let createTools: [Tool] = [
+ .select, .arrow, .rectangle, .ellipse, .text, .pencil, .censor,
+ .number, .stamp, .highlighter, .measure, .spotlight,
+ ]
+ private static let utilityTools: [Tool] = [.loupe, .eyedropper]
+
// MARK: Row 1 β Tools
private var toolsRow: some View {
HStack(spacing: 2) {
- ForEach(Tool.allCases.filter { !$0.isUtility }) { toolButton($0) }
+ toolGroupLabel("Create")
+ ForEach(Self.createTools) { toolButton($0) }
Divider().frame(height: 22).padding(.horizontal, 4)
- ForEach(Tool.allCases.filter { $0.isUtility }) { toolButton($0) }
+ toolGroupLabel("Utility")
+ ForEach(Self.utilityTools) { toolButton($0) }
Spacer(minLength: 6)
contextualControls
}
@@ -37,6 +46,13 @@ struct EditorView: View {
.padding(.vertical, 7)
}
+ private func toolGroupLabel(_ title: String) -> some View {
+ Text(title)
+ .font(.system(size: 10, weight: .semibold))
+ .foregroundStyle(.secondary)
+ .padding(.trailing, 2)
+ }
+
private func toolButton(_ tool: Tool) -> some View {
Button {
model.selectTool(tool)
@@ -74,7 +90,7 @@ struct EditorView: View {
}
.pickerStyle(.segmented)
.labelsHidden()
- .frame(width: 108)
+ .frame(width: 142)
.help("Censor mode")
}
@@ -118,6 +134,8 @@ struct EditorView: View {
Divider().frame(height: 22)
effects
Divider().frame(height: 22)
+ layers
+ Divider().frame(height: 22)
history
Spacer(minLength: 8)
output
@@ -173,6 +191,48 @@ struct EditorView: View {
}
}
+ private var layers: some View {
+ HStack(spacing: 2) {
+ Button { model.sendToBack() } label: { Image(systemName: "square.3.layers.3d.bottom.filled") }
+ .disabled(model.selectedID == nil)
+ .help("Send to back")
+ Button { model.sendBackward() } label: { Image(systemName: "square.2.layers.3d.bottom.filled") }
+ .disabled(model.selectedID == nil)
+ .help("Send backward")
+ Button { model.bringForward() } label: { Image(systemName: "square.2.layers.3d.top.filled") }
+ .disabled(model.selectedID == nil)
+ .help("Bring forward")
+ Button { model.bringToFront() } label: { Image(systemName: "square.3.layers.3d.top.filled") }
+ .disabled(model.selectedID == nil)
+ .help("Bring to front")
+ }
+ .buttonStyle(.borderless)
+ }
+
+ private var statusStrip: some View {
+ HStack(spacing: 12) {
+ Text("\(Int(model.pointSize.width))Γ\(Int(model.pointSize.height)) pt")
+ .font(.system(size: 11, design: .monospaced))
+ .foregroundStyle(.secondary)
+ Text(model.activeTool.label)
+ .font(.system(size: 11, weight: .medium))
+ .foregroundStyle(.secondary)
+ if model.isUploading {
+ ProgressView().controlSize(.mini)
+ }
+ if let message = model.statusMessage {
+ Text(message)
+ .font(.system(size: 11))
+ .foregroundStyle(.secondary)
+ .lineLimit(1)
+ }
+ Spacer()
+ }
+ .padding(.horizontal, 12)
+ .padding(.vertical, 5)
+ .background(Color(nsColor: .windowBackgroundColor))
+ }
+
private var history: some View {
HStack(spacing: 2) {
Button { model.undo() } label: { Image(systemName: "arrow.uturn.backward") }
@@ -227,6 +287,14 @@ struct EditorView: View {
}
.keyboardShortcut("s", modifiers: .command)
+ Button {
+ model.uploadCapture()
+ } label: {
+ Label("Upload", systemImage: "icloud.and.arrow.up")
+ }
+ .disabled(model.isUploading || !UploadPreferences.isConfigured)
+ .help(UploadPreferences.isConfigured ? "Upload to Supabase and copy link" : "Configure Supabase in Preferences")
+
Button(action: onShowHistory) {
Image(systemName: "clock.arrow.circlepath")
}
@@ -314,6 +382,21 @@ private struct VisionPanel: View {
Spacer()
Button("Copy Text") { model.copyRecognizedText() }
}
+ if TranslationService.isAvailable {
+ Button("Translate On-Device") { model.translateRecognizedText() }
+ .disabled(model.isTranslating)
+ }
+ if model.isTranslating {
+ ProgressView().controlSize(.small)
+ }
+ if let translated = model.translatedText, !translated.isEmpty {
+ Divider()
+ Text(translated)
+ .font(.footnote)
+ .textSelection(.enabled)
+ .frame(maxHeight: 80)
+ Button("Copy Translation") { model.copyTranslatedText() }
+ }
}
if !model.visionAnalysis.piiText.isEmpty {
Button("Censor Detected Sensitive Text") { model.censorDetectedPII() }
diff --git a/Sources/Notable/Editor/EditorWindowController.swift b/Sources/Parcel/Editor/EditorWindowController.swift
similarity index 99%
rename from Sources/Notable/Editor/EditorWindowController.swift
rename to Sources/Parcel/Editor/EditorWindowController.swift
index a7a1d8b..b3c4612 100644
--- a/Sources/Notable/Editor/EditorWindowController.swift
+++ b/Sources/Parcel/Editor/EditorWindowController.swift
@@ -50,7 +50,7 @@ final class EditorWindowController: NSObject, NSWindowDelegate {
backing: .buffered,
defer: false
)
- window.title = "Notable"
+ window.title = "Parcel"
window.isReleasedWhenClosed = false
window.tabbingMode = .disallowed
window.center()
diff --git a/Sources/Notable/Editor/OutputFormat.swift b/Sources/Parcel/Editor/OutputFormat.swift
similarity index 100%
rename from Sources/Notable/Editor/OutputFormat.swift
rename to Sources/Parcel/Editor/OutputFormat.swift
diff --git a/Sources/Notable/Editor/UtilityOverlays.swift b/Sources/Parcel/Editor/UtilityOverlays.swift
similarity index 100%
rename from Sources/Notable/Editor/UtilityOverlays.swift
rename to Sources/Parcel/Editor/UtilityOverlays.swift
diff --git a/Sources/Notable/History/HistoryStore.swift b/Sources/Parcel/History/HistoryStore.swift
similarity index 93%
rename from Sources/Notable/History/HistoryStore.swift
rename to Sources/Parcel/History/HistoryStore.swift
index 69bb59d..77329a0 100644
--- a/Sources/Notable/History/HistoryStore.swift
+++ b/Sources/Parcel/History/HistoryStore.swift
@@ -101,7 +101,7 @@ final class HistoryStore: ObservableObject {
self.fileManager = fileManager
let appSupport = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
rootURL = appSupport
- .appendingPathComponent("io.notable.Notable", isDirectory: true)
+ .appendingPathComponent(AppIdentity.appSupportComponent, isDirectory: true)
.appendingPathComponent("History", isDirectory: true)
indexURL = rootURL.appendingPathComponent("index.json")
createRootIfNeeded()
@@ -146,7 +146,7 @@ final class HistoryStore: ObservableObject {
writeIndex()
return id
} catch {
- NSLog("Notable: could not create local history entry β \(error)")
+ NSLog("Parcel: could not create local history entry β \(error)")
return nil
}
}
@@ -162,7 +162,7 @@ final class HistoryStore: ObservableObject {
writeIndex()
}
} catch {
- NSLog("Notable: could not save local history entry β \(error)")
+ NSLog("Parcel: could not save local history entry β \(error)")
}
}
@@ -178,7 +178,7 @@ final class HistoryStore: ObservableObject {
document: document
)
} catch {
- NSLog("Notable: could not restore local history entry β \(error)")
+ NSLog("Parcel: could not restore local history entry β \(error)")
return nil
}
}
@@ -201,7 +201,7 @@ final class HistoryStore: ObservableObject {
entries.removeAll { $0.id == id }
writeIndex()
} catch {
- NSLog("Notable: could not remove local history entry β \(error)")
+ NSLog("Parcel: could not remove local history entry β \(error)")
}
}
@@ -215,7 +215,7 @@ final class HistoryStore: ObservableObject {
do {
try fileManager.createDirectory(at: rootURL, withIntermediateDirectories: true)
} catch {
- NSLog("Notable: could not create history directory β \(error)")
+ NSLog("Parcel: could not create history directory β \(error)")
}
}
@@ -224,7 +224,7 @@ final class HistoryStore: ObservableObject {
do {
entries = try read([HistoryEntry].self, from: indexURL).sorted { $0.updatedAt > $1.updatedAt }
} catch {
- NSLog("Notable: could not read local history index β \(error)")
+ NSLog("Parcel: could not read local history index β \(error)")
}
}
@@ -232,7 +232,7 @@ final class HistoryStore: ObservableObject {
do {
try write(entries, to: indexURL)
} catch {
- NSLog("Notable: could not write local history index β \(error)")
+ NSLog("Parcel: could not write local history index β \(error)")
}
}
@@ -271,7 +271,7 @@ final class BrandKitStore: ObservableObject {
static let shared = BrandKitStore()
@Published private(set) var kits: [BrandKit] = []
- private let defaultsKey = "io.notable.brandKits"
+ private let defaultsKey = "\(AppIdentity.defaultsPrefix).brandKits"
private init() {
guard let data = UserDefaults.standard.data(forKey: defaultsKey) else { return }
diff --git a/Sources/Notable/History/HistoryWindowController.swift b/Sources/Parcel/History/HistoryWindowController.swift
similarity index 100%
rename from Sources/Notable/History/HistoryWindowController.swift
rename to Sources/Parcel/History/HistoryWindowController.swift
diff --git a/Sources/Notable/Hotkeys/HotKeyManager.swift b/Sources/Parcel/Hotkeys/HotKeyManager.swift
similarity index 93%
rename from Sources/Notable/Hotkeys/HotKeyManager.swift
rename to Sources/Parcel/Hotkeys/HotKeyManager.swift
index f8a8dad..da0ff77 100644
--- a/Sources/Notable/Hotkeys/HotKeyManager.swift
+++ b/Sources/Parcel/Hotkeys/HotKeyManager.swift
@@ -18,7 +18,7 @@ final class HotKeyManager {
/// Default capture shortcut: ββ§2 (ββ§3/4/5 belong to the macOS screenshot service).
func registerDefault() {
- register(keyCode: UInt32(kVK_ANSI_2), modifiers: UInt32(cmdKey | shiftKey))
+ register(keyCode: HotKeyPreferences.keyCode, modifiers: HotKeyPreferences.modifiers)
}
func register(keyCode: UInt32, modifiers: UInt32) {
@@ -33,7 +33,7 @@ final class HotKeyManager {
if status == noErr {
hotKeyRef = ref
} else {
- NSLog("Notable: RegisterEventHotKey failed (status \(status))")
+ NSLog("Parcel: RegisterEventHotKey failed (status \(status))")
}
}
diff --git a/Sources/Parcel/Hotkeys/HotKeyPreferences.swift b/Sources/Parcel/Hotkeys/HotKeyPreferences.swift
new file mode 100644
index 0000000..4421f38
--- /dev/null
+++ b/Sources/Parcel/Hotkeys/HotKeyPreferences.swift
@@ -0,0 +1,93 @@
+import Carbon.HIToolbox
+import Foundation
+
+/// Persisted global Capture hotkey. Defaults to ββ§2.
+enum HotKeyPreferences {
+ private static let keyCodeKey = "\(AppIdentity.defaultsPrefix).hotkey.keyCode"
+ private static let modifiersKey = "\(AppIdentity.defaultsPrefix).hotkey.modifiers"
+
+ static var keyCode: UInt32 {
+ get {
+ let stored = UserDefaults.standard.integer(forKey: keyCodeKey)
+ return stored == 0 ? UInt32(kVK_ANSI_2) : UInt32(stored)
+ }
+ set { UserDefaults.standard.set(Int(newValue), forKey: keyCodeKey) }
+ }
+
+ static var modifiers: UInt32 {
+ get {
+ if UserDefaults.standard.object(forKey: modifiersKey) == nil {
+ return UInt32(cmdKey | shiftKey)
+ }
+ return UInt32(UserDefaults.standard.integer(forKey: modifiersKey))
+ }
+ set { UserDefaults.standard.set(Int(newValue), forKey: modifiersKey) }
+ }
+
+ static var displayString: String {
+ HotKeyDisplay.string(keyCode: keyCode, modifiers: modifiers)
+ }
+
+ static func resetToDefault() {
+ keyCode = UInt32(kVK_ANSI_2)
+ modifiers = UInt32(cmdKey | shiftKey)
+ }
+}
+
+/// Human-readable shortcut labels for Preferences and the marketing site.
+enum HotKeyDisplay {
+ static func string(keyCode: UInt32, modifiers: UInt32) -> String {
+ var parts: [String] = []
+ if modifiers & UInt32(controlKey) != 0 { parts.append("β") }
+ if modifiers & UInt32(optionKey) != 0 { parts.append("β₯") }
+ if modifiers & UInt32(shiftKey) != 0 { parts.append("β§") }
+ if modifiers & UInt32(cmdKey) != 0 { parts.append("β") }
+ parts.append(keyLabel(for: keyCode))
+ return parts.joined()
+ }
+
+ private static func keyLabel(for keyCode: UInt32) -> String {
+ switch Int(keyCode) {
+ case kVK_ANSI_A: return "A"
+ case kVK_ANSI_B: return "B"
+ case kVK_ANSI_C: return "C"
+ case kVK_ANSI_D: return "D"
+ case kVK_ANSI_E: return "E"
+ case kVK_ANSI_F: return "F"
+ case kVK_ANSI_G: return "G"
+ case kVK_ANSI_H: return "H"
+ case kVK_ANSI_I: return "I"
+ case kVK_ANSI_J: return "J"
+ case kVK_ANSI_K: return "K"
+ case kVK_ANSI_L: return "L"
+ case kVK_ANSI_M: return "M"
+ case kVK_ANSI_N: return "N"
+ case kVK_ANSI_O: return "O"
+ case kVK_ANSI_P: return "P"
+ case kVK_ANSI_Q: return "Q"
+ case kVK_ANSI_R: return "R"
+ case kVK_ANSI_S: return "S"
+ case kVK_ANSI_T: return "T"
+ case kVK_ANSI_U: return "U"
+ case kVK_ANSI_V: return "V"
+ case kVK_ANSI_W: return "W"
+ case kVK_ANSI_X: return "X"
+ case kVK_ANSI_Y: return "Y"
+ case kVK_ANSI_Z: return "Z"
+ case kVK_ANSI_0: return "0"
+ case kVK_ANSI_1: return "1"
+ case kVK_ANSI_2: return "2"
+ case kVK_ANSI_3: return "3"
+ case kVK_ANSI_4: return "4"
+ case kVK_ANSI_5: return "5"
+ case kVK_ANSI_6: return "6"
+ case kVK_ANSI_7: return "7"
+ case kVK_ANSI_8: return "8"
+ case kVK_ANSI_9: return "9"
+ case kVK_Space: return "Space"
+ case kVK_Return: return "Return"
+ case kVK_Escape: return "Esc"
+ default: return "Key \(keyCode)"
+ }
+ }
+}
diff --git a/Sources/Notable/MenuBar/MenuBarContent.swift b/Sources/Parcel/MenuBar/MenuBarContent.swift
similarity index 80%
rename from Sources/Notable/MenuBar/MenuBarContent.swift
rename to Sources/Parcel/MenuBar/MenuBarContent.swift
index b167e77..c6ad117 100644
--- a/Sources/Notable/MenuBar/MenuBarContent.swift
+++ b/Sources/Parcel/MenuBar/MenuBarContent.swift
@@ -1,8 +1,9 @@
import SwiftUI
-/// The menu shown from Notable's menu bar item.
+/// The menu shown from Parcel's menu bar item.
struct MenuBarContent: View {
@ObservedObject var coordinator: AppCoordinator
+ var onCheckForUpdates: () -> Void
var body: some View {
Button("Capture Region") {
@@ -60,6 +61,20 @@ struct MenuBarContent: View {
}
.disabled(coordinator.isStartingRecording || coordinator.isCapturing)
+ Menu("Recording FPS") {
+ ForEach(RecordingFPS.allCases) { fps in
+ Button {
+ RecordingFPS.current = fps
+ } label: {
+ if RecordingFPS.current == fps {
+ Label(fps.label, systemImage: "checkmark")
+ } else {
+ Text(fps.label)
+ }
+ }
+ }
+ }
+
Divider()
Button("Preferencesβ¦") {
@@ -72,9 +87,13 @@ struct MenuBarContent: View {
}
.keyboardShortcut("h", modifiers: [.command, .shift])
+ Button("Check for Updatesβ¦") {
+ onCheckForUpdates()
+ }
+
Divider()
- Button("Quit Notable") {
+ Button("Quit Parcel") {
NSApplication.shared.terminate(nil)
}
.keyboardShortcut("q", modifiers: .command)
diff --git a/Sources/Parcel/Onboarding/WelcomeWindowController.swift b/Sources/Parcel/Onboarding/WelcomeWindowController.swift
new file mode 100644
index 0000000..b762e77
--- /dev/null
+++ b/Sources/Parcel/Onboarding/WelcomeWindowController.swift
@@ -0,0 +1,159 @@
+import AppKit
+import SwiftUI
+
+@MainActor
+final class WelcomeWindowController {
+
+ private var window: NSWindow?
+ private let onOpenPreferences: () -> Void
+ private let onFinish: () -> Void
+
+ init(onOpenPreferences: @escaping () -> Void, onFinish: @escaping () -> Void) {
+ self.onOpenPreferences = onOpenPreferences
+ self.onFinish = onFinish
+ }
+
+ func show() {
+ if window == nil {
+ let view = WelcomeView(
+ onGrantPermission: {
+ ScreenRecordingPermission.request()
+ ScreenRecordingPermission.openSystemSettings()
+ },
+ onOpenPreferences: { [weak self] in
+ self?.onOpenPreferences()
+ },
+ onQuitAndReopen: {
+ NSApplication.shared.terminate(nil)
+ },
+ onFinish: { [weak self] in
+ self?.onFinish()
+ self?.window?.close()
+ }
+ )
+ let hosting = NSHostingView(rootView: view)
+ let window = NSWindow(
+ contentRect: NSRect(x: 0, y: 0, width: 520, height: 440),
+ styleMask: [.titled, .closable],
+ backing: .buffered,
+ defer: false
+ )
+ window.title = "Welcome to Parcel"
+ window.contentView = hosting
+ window.center()
+ window.isReleasedWhenClosed = false
+ self.window = window
+ }
+ NSApp.activate(ignoringOtherApps: true)
+ window?.makeKeyAndOrderFront(nil)
+ }
+}
+
+struct WelcomeView: View {
+ @State private var step = 0
+ @State private var hasPermission = ScreenRecordingPermission.isGranted
+
+ let onGrantPermission: () -> Void
+ let onOpenPreferences: () -> Void
+ let onQuitAndReopen: () -> Void
+ let onFinish: () -> Void
+
+ private let poll = Timer.publish(every: 1.5, on: .main, in: .common).autoconnect()
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 20) {
+ switch step {
+ case 0:
+ welcomeStep
+ case 1:
+ permissionStep
+ case 2:
+ uploadStep
+ default:
+ finishStep
+ }
+
+ HStack {
+ if step > 0 {
+ Button("Back") { step -= 1 }
+ }
+ Spacer()
+ if step < 3 {
+ Button(step == 2 ? "Skip" : "Continue") { step += 1 }
+ .keyboardShortcut(.defaultAction)
+ } else {
+ Button("Get Started") {
+ onFinish()
+ }
+ .keyboardShortcut(.defaultAction)
+ }
+ }
+ }
+ .padding(28)
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+ .onReceive(poll) { _ in
+ Task { @MainActor in
+ hasPermission = await ScreenRecordingPermission.hasEffectiveAccess()
+ }
+ }
+ }
+
+ private var welcomeStep: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ Text("Welcome to Parcel")
+ .font(.title.bold())
+ Text("Capture, mark up, and ship β entirely on your Mac.")
+ .foregroundStyle(.secondary)
+ Text("Parcel is local-first: OCR, face detection, translation, and redaction use Apple on-device frameworks only. Nothing leaves your Mac unless you choose to upload.")
+ .fixedSize(horizontal: false, vertical: true)
+ }
+ }
+
+ private var permissionStep: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ Text("Screen Recording Permission")
+ .font(.title2.bold())
+ Text("macOS requires Screen Recording access before Parcel can Capture or record your display.")
+ .foregroundStyle(.secondary)
+ .fixedSize(horizontal: false, vertical: true)
+ HStack {
+ if hasPermission {
+ Label("Granted", systemImage: "checkmark.circle.fill")
+ .foregroundStyle(.green)
+ } else {
+ Label("Not granted yet", systemImage: "exclamationmark.circle")
+ .foregroundStyle(.orange)
+ }
+ }
+ HStack(spacing: 12) {
+ Button("Grant Permissionβ¦") { onGrantPermission() }
+ Button("Quit & Reopen") { onQuitAndReopen() }
+ }
+ Text("After granting in System Settings, quit and reopen Parcel so the permission takes effect.")
+ .font(.footnote)
+ .foregroundStyle(.secondary)
+ }
+ }
+
+ private var uploadStep: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ Text("Optional: Supabase Upload")
+ .font(.title2.bold())
+ Text("Configure a Supabase Storage bucket in Preferences to upload Captures and copy a public link from the Editor.")
+ .foregroundStyle(.secondary)
+ .fixedSize(horizontal: false, vertical: true)
+ Button("Open Preferencesβ¦") { onOpenPreferences() }
+ }
+ }
+
+ private var finishStep: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ Text("You're Ready")
+ .font(.title2.bold())
+ Text("Press ββ§2 from anywhere to start a Capture. Parcel lives in your menu bar β look for the Parcel icon.")
+ .fixedSize(horizontal: false, vertical: true)
+ Text("You can change the shortcut anytime in Preferences.")
+ .foregroundStyle(.secondary)
+ }
+ }
+}
diff --git a/Sources/Notable/Permissions/ScreenRecordingPermission.swift b/Sources/Parcel/Permissions/ScreenRecordingPermission.swift
similarity index 86%
rename from Sources/Notable/Permissions/ScreenRecordingPermission.swift
rename to Sources/Parcel/Permissions/ScreenRecordingPermission.swift
index dac055c..6a00e2d 100644
--- a/Sources/Notable/Permissions/ScreenRecordingPermission.swift
+++ b/Sources/Parcel/Permissions/ScreenRecordingPermission.swift
@@ -41,6 +41,13 @@ enum ScreenRecordingPermission {
}
}
+ /// Best-effort permission probe for UI and preflight checks. Preflight is fast but can lag
+ /// behind the actual ScreenCaptureKit grant; the capture API probe is the fallback.
+ static func hasEffectiveAccess() async -> Bool {
+ if isGranted { return true }
+ return await canUseCaptureAPI()
+ }
+
/// Deep-link to the Screen Recording pane in System Settings.
static func openSystemSettings() {
let urlString = "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"
diff --git a/Sources/Parcel/Preferences/HotKeyRecorder.swift b/Sources/Parcel/Preferences/HotKeyRecorder.swift
new file mode 100644
index 0000000..9560ab5
--- /dev/null
+++ b/Sources/Parcel/Preferences/HotKeyRecorder.swift
@@ -0,0 +1,66 @@
+import AppKit
+import Carbon.HIToolbox
+import SwiftUI
+
+/// Captures the next key-down event as a global hotkey binding.
+struct HotKeyRecorder: View {
+ @Binding var keyCode: UInt32
+ @Binding var modifiers: UInt32
+ @State private var isRecording = false
+
+ var body: some View {
+ HStack {
+ Text(isRecording ? "Press a shortcutβ¦" : HotKeyDisplay.string(keyCode: keyCode, modifiers: modifiers))
+ .font(.system(.body, design: .monospaced))
+ .frame(maxWidth: .infinity, alignment: .leading)
+ Button(isRecording ? "Cancel" : "Recordβ¦") {
+ isRecording.toggle()
+ if isRecording { installMonitor() }
+ }
+ if !isRecording {
+ Button("Reset") { HotKeyPreferences.resetToDefault(); reload() }
+ }
+ }
+ .onDisappear { removeMonitor() }
+ }
+
+ private static var monitor: Any?
+
+ private func reload() {
+ keyCode = HotKeyPreferences.keyCode
+ modifiers = HotKeyPreferences.modifiers
+ }
+
+ private func installMonitor() {
+ removeMonitor()
+ Self.monitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
+ guard isRecording else { return event }
+ let flags = event.modifierFlags.intersection([.command, .shift, .option, .control])
+ guard !flags.isEmpty else { return nil }
+ var carbonMods: UInt32 = 0
+ if flags.contains(.control) { carbonMods |= UInt32(controlKey) }
+ if flags.contains(.option) { carbonMods |= UInt32(optionKey) }
+ if flags.contains(.shift) { carbonMods |= UInt32(shiftKey) }
+ if flags.contains(.command) { carbonMods |= UInt32(cmdKey) }
+ keyCode = UInt32(event.keyCode)
+ modifiers = carbonMods
+ HotKeyPreferences.keyCode = keyCode
+ HotKeyPreferences.modifiers = modifiers
+ isRecording = false
+ removeMonitor()
+ NotificationCenter.default.post(name: .hotKeyPreferencesDidChange, object: nil)
+ return nil
+ }
+ }
+
+ private func removeMonitor() {
+ if let monitor = Self.monitor {
+ NSEvent.removeMonitor(monitor)
+ Self.monitor = nil
+ }
+ }
+}
+
+extension Notification.Name {
+ static let hotKeyPreferencesDidChange = Notification.Name("dev.parable.hotKeyPreferencesDidChange")
+}
diff --git a/Sources/Parcel/Preferences/PreferencesView.swift b/Sources/Parcel/Preferences/PreferencesView.swift
new file mode 100644
index 0000000..c1f7cc0
--- /dev/null
+++ b/Sources/Parcel/Preferences/PreferencesView.swift
@@ -0,0 +1,75 @@
+import SwiftUI
+
+/// Preferences: permissions, configurable Capture hotkey, and Supabase upload settings.
+struct PreferencesView: View {
+ @State private var hasScreenPermission = ScreenRecordingPermission.isGranted
+ @State private var keyCode = HotKeyPreferences.keyCode
+ @State private var modifiers = HotKeyPreferences.modifiers
+ @State private var supabaseURL = UploadPreferences.supabaseURL
+ @State private var anonKey = UploadPreferences.anonKey
+ @State private var bucketName = UploadPreferences.bucketName
+ @State private var publicBaseURL = UploadPreferences.publicBaseURL
+
+ private let poll = Timer.publish(every: 1.5, on: .main, in: .common).autoconnect()
+
+ var body: some View {
+ Form {
+ Section("Permissions") {
+ HStack {
+ VStack(alignment: .leading, spacing: 2) {
+ Text("Screen Recording")
+ Text("Required to capture your screen. After granting, quit and reopen Parcel.")
+ .font(.footnote)
+ .foregroundStyle(.secondary)
+ }
+ Spacer()
+ if hasScreenPermission {
+ Label("Granted", systemImage: "checkmark.circle.fill")
+ .foregroundStyle(.green)
+ .labelStyle(.titleAndIcon)
+ } else {
+ Button("Grantβ¦") {
+ ScreenRecordingPermission.request()
+ ScreenRecordingPermission.openSystemSettings()
+ }
+ }
+ }
+ }
+
+ Section("Shortcut") {
+ HotKeyRecorder(keyCode: $keyCode, modifiers: $modifiers)
+ }
+
+ Section("Upload (Supabase)") {
+ TextField("Project URL", text: $supabaseURL)
+ .textFieldStyle(.roundedBorder)
+ SecureField("Anon key", text: $anonKey)
+ .textFieldStyle(.roundedBorder)
+ TextField("Bucket name", text: $bucketName)
+ .textFieldStyle(.roundedBorder)
+ TextField("Public base URL (optional)", text: $publicBaseURL)
+ .textFieldStyle(.roundedBorder)
+ Text("Configure a public Supabase Storage bucket. Upload copies the object URL to your clipboard.")
+ .font(.footnote)
+ .foregroundStyle(.secondary)
+ }
+ }
+ .formStyle(.grouped)
+ .frame(width: 480, height: 420)
+ .task { await refreshPermissionStatus() }
+ .onReceive(poll) { _ in Task { await refreshPermissionStatus() } }
+ .onDisappear { persistUploadSettings() }
+ }
+
+ private func persistUploadSettings() {
+ UploadPreferences.supabaseURL = supabaseURL
+ UploadPreferences.anonKey = anonKey
+ UploadPreferences.bucketName = bucketName
+ UploadPreferences.publicBaseURL = publicBaseURL
+ }
+
+ @MainActor
+ private func refreshPermissionStatus() async {
+ hasScreenPermission = await ScreenRecordingPermission.hasEffectiveAccess()
+ }
+}
diff --git a/Sources/Notable/Preferences/PreferencesWindowController.swift b/Sources/Parcel/Preferences/PreferencesWindowController.swift
similarity index 95%
rename from Sources/Notable/Preferences/PreferencesWindowController.swift
rename to Sources/Parcel/Preferences/PreferencesWindowController.swift
index b749e06..a585759 100644
--- a/Sources/Notable/Preferences/PreferencesWindowController.swift
+++ b/Sources/Parcel/Preferences/PreferencesWindowController.swift
@@ -20,7 +20,7 @@ final class PreferencesWindowController {
backing: .buffered,
defer: false
)
- window.title = "Notable Preferences"
+ window.title = "Parcel Preferences"
window.isReleasedWhenClosed = false
window.contentView = NSHostingView(rootView: PreferencesView())
window.center()
diff --git a/Sources/Parcel/Recording/RecordingPreferences.swift b/Sources/Parcel/Recording/RecordingPreferences.swift
new file mode 100644
index 0000000..3d1ac26
--- /dev/null
+++ b/Sources/Parcel/Recording/RecordingPreferences.swift
@@ -0,0 +1,26 @@
+import CoreMedia
+import Foundation
+
+enum RecordingFPS: Int, CaseIterable, Identifiable {
+ case fps30 = 30
+ case fps60 = 60
+ case fps120 = 120
+
+ var id: Int { rawValue }
+
+ var label: String { "\(rawValue) fps" }
+
+ var frameInterval: CMTime {
+ CMTime(value: 1, timescale: CMTimeScale(rawValue))
+ }
+
+ private static let storageKey = "\(AppIdentity.defaultsPrefix).recording.fps"
+
+ static var current: RecordingFPS {
+ get {
+ let stored = UserDefaults.standard.integer(forKey: storageKey)
+ return RecordingFPS(rawValue: stored) ?? .fps60
+ }
+ set { UserDefaults.standard.set(newValue.rawValue, forKey: storageKey) }
+ }
+}
diff --git a/Sources/Notable/Recording/RecordingWindowController.swift b/Sources/Parcel/Recording/RecordingWindowController.swift
similarity index 99%
rename from Sources/Notable/Recording/RecordingWindowController.swift
rename to Sources/Parcel/Recording/RecordingWindowController.swift
index 957677e..d964907 100644
--- a/Sources/Notable/Recording/RecordingWindowController.swift
+++ b/Sources/Parcel/Recording/RecordingWindowController.swift
@@ -140,7 +140,7 @@ private final class RecordingEditorModel: ObservableObject {
private func defaultName(extension fileExtension: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd 'at' HH.mm.ss"
- return "Notable Recording \(formatter.string(from: Date())).\(fileExtension)"
+ return "Parcel Recording \(formatter.string(from: Date())).\(fileExtension)"
}
private enum ExportError: LocalizedError {
diff --git a/Sources/Notable/Recording/ScreenRecorder.swift b/Sources/Parcel/Recording/ScreenRecorder.swift
similarity index 95%
rename from Sources/Notable/Recording/ScreenRecorder.swift
rename to Sources/Parcel/Recording/ScreenRecorder.swift
index bda5ebe..b567413 100644
--- a/Sources/Notable/Recording/ScreenRecorder.swift
+++ b/Sources/Parcel/Recording/ScreenRecorder.swift
@@ -9,7 +9,7 @@ import ScreenCaptureKit
@MainActor
final class ScreenRecorder: NSObject, ObservableObject {
- enum RecorderError: LocalizedError {
+ enum RecorderError: LocalizedError, Equatable {
case noDisplay
case alreadyRecording
case notRecording
@@ -47,7 +47,9 @@ final class ScreenRecorder: NSObject, ObservableObject {
do {
content = try await SCShareableContent.excludingDesktopWindows(false, onScreenWindowsOnly: true)
} catch {
- if !ScreenRecordingPermission.isGranted { throw RecorderError.permissionDenied }
+ if !(await ScreenRecordingPermission.hasEffectiveAccess()) {
+ throw RecorderError.permissionDenied
+ }
throw error
}
guard let display = preferredDisplay(in: content) else { throw RecorderError.noDisplay }
@@ -56,7 +58,7 @@ final class ScreenRecorder: NSObject, ObservableObject {
let configuration = SCStreamConfiguration()
configuration.width = Int((CGFloat(display.width) * scale).rounded())
configuration.height = Int((CGFloat(display.height) * scale).rounded())
- configuration.minimumFrameInterval = CMTime(value: 1, timescale: 60)
+ configuration.minimumFrameInterval = RecordingFPS.current.frameInterval
configuration.queueDepth = 5
configuration.showsCursor = true
configuration.capturesAudio = true
@@ -179,7 +181,7 @@ private final class NativeRecordingDelegate: NSObject, SCRecordingOutputDelegate
/// AVFoundation fallback used on macOS 13β14. The stream is owned by `ScreenRecorder`; this
/// object only serializes sample delivery and finalization for one MP4 file.
private final class LegacyRecordingWriter: NSObject, SCStreamOutput, @unchecked Sendable {
- let sampleQueue = DispatchQueue(label: "io.notable.recording.samples")
+ let sampleQueue = DispatchQueue(label: "dev.parable.recording.samples")
private let writer: AVAssetWriter
private let videoInput: AVAssetWriterInput
@@ -210,7 +212,7 @@ private final class LegacyRecordingWriter: NSObject, SCStreamOutput, @unchecked
videoInput.expectsMediaDataInRealTime = true
audioInput.expectsMediaDataInRealTime = true
guard writer.canAdd(videoInput), writer.canAdd(audioInput) else {
- throw NSError(domain: "io.notable.recording", code: 1, userInfo: [
+ throw NSError(domain: "dev.parable.recording", code: 1, userInfo: [
NSLocalizedDescriptionKey: "The MP4 writer could not accept its media inputs.",
])
}
diff --git a/Sources/Notable/Resources/Assets.xcassets/AccentColor.colorset/Contents.json b/Sources/Parcel/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
similarity index 100%
rename from Sources/Notable/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
rename to Sources/Parcel/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..7e1a770
--- /dev/null
+++ b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,15 @@
+{
+ "images" : [
+ { "filename" : "icon_16x16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" },
+ { "filename" : "icon_16x16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" },
+ { "filename" : "icon_32x32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" },
+ { "filename" : "icon_32x32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" },
+ { "filename" : "icon_128x128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" },
+ { "filename" : "icon_128x128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" },
+ { "filename" : "icon_256x256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" },
+ { "filename" : "icon_256x256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" },
+ { "filename" : "icon_512x512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" },
+ { "filename" : "icon_512x512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" }
+ ],
+ "info" : { "author" : "xcode", "version" : 1 }
+}
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
new file mode 100644
index 0000000..e04ac7e
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
new file mode 100644
index 0000000..52796ab
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
new file mode 100644
index 0000000..63d8f6b
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
new file mode 100644
index 0000000..91e9a68
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
new file mode 100644
index 0000000..52796ab
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
new file mode 100644
index 0000000..2eab491
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
new file mode 100644
index 0000000..91e9a68
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
new file mode 100644
index 0000000..6eaff76
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
new file mode 100644
index 0000000..2eab491
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
new file mode 100644
index 0000000..cd35662
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png differ
diff --git a/Sources/Notable/Resources/Assets.xcassets/Contents.json b/Sources/Parcel/Resources/Assets.xcassets/Contents.json
similarity index 100%
rename from Sources/Notable/Resources/Assets.xcassets/Contents.json
rename to Sources/Parcel/Resources/Assets.xcassets/Contents.json
diff --git a/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/Contents.json b/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/Contents.json
new file mode 100644
index 0000000..c4c3e99
--- /dev/null
+++ b/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/Contents.json
@@ -0,0 +1,8 @@
+{
+ "images" : [
+ { "filename" : "MenuBarIcon.png", "idiom" : "mac", "scale" : "1x" },
+ { "filename" : "MenuBarIcon@2x.png", "idiom" : "mac", "scale" : "2x" }
+ ],
+ "info" : { "author" : "xcode", "version" : 1 },
+ "properties" : { "template-rendering-intent" : "template" }
+}
diff --git a/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/MenuBarIcon.png b/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/MenuBarIcon.png
new file mode 100644
index 0000000..a49ced3
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/MenuBarIcon.png differ
diff --git a/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/MenuBarIcon@2x.png b/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/MenuBarIcon@2x.png
new file mode 100644
index 0000000..486589c
Binary files /dev/null and b/Sources/Parcel/Resources/Assets.xcassets/MenuBarIcon.imageset/MenuBarIcon@2x.png differ
diff --git a/Sources/Notable/Resources/Info.plist b/Sources/Parcel/Resources/Info.plist
similarity index 67%
rename from Sources/Notable/Resources/Info.plist
rename to Sources/Parcel/Resources/Info.plist
index d1fb60b..bee2b95 100644
--- a/Sources/Notable/Resources/Info.plist
+++ b/Sources/Parcel/Resources/Info.plist
@@ -13,7 +13,7 @@
CFBundleName
$(PRODUCT_NAME)
CFBundleDisplayName
- Notable
+ Parcel
CFBundlePackageType
APPL
CFBundleShortVersionString
@@ -22,13 +22,21 @@
$(CURRENT_PROJECT_VERSION)
LSMinimumSystemVersion
$(MACOSX_DEPLOYMENT_TARGET)
+ LSApplicationCategoryType
+ public.app-category.productivity
LSUIElement
NSHighResolutionCapable
+ NSMicrophoneUsageDescription
+ Parcel can include microphone audio in screen recordings when you start a recording.
NSPrincipalClass
NSApplication
NSHumanReadableCopyright
- Copyright Β© 2026 Notable Project Contributors. MIT Licensed.
+ Copyright Β© 2026 Parable. MIT Licensed.
+ SUFeedURL
+ https://parcel.parable.dev/appcast.xml
+ SUPublicEDKey
+ REPLACE_WITH_SPARKLE_EDDSA_PUBLIC_KEY
diff --git a/Sources/Parcel/Resources/Parcel.entitlements b/Sources/Parcel/Resources/Parcel.entitlements
new file mode 100644
index 0000000..19afff1
--- /dev/null
+++ b/Sources/Parcel/Resources/Parcel.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.files.user-selected.read-write
+
+
+
diff --git a/Sources/Parcel/Resources/PrivacyInfo.xcprivacy b/Sources/Parcel/Resources/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000..215a9bb
--- /dev/null
+++ b/Sources/Parcel/Resources/PrivacyInfo.xcprivacy
@@ -0,0 +1,31 @@
+
+
+
+
+ NSPrivacyTracking
+
+ NSPrivacyTrackingDomains
+
+ NSPrivacyCollectedDataTypes
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ CA92.1
+
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryFileTimestamp
+ NSPrivacyAccessedAPITypeReasons
+
+ C617.1
+
+
+
+
+
diff --git a/Sources/Parcel/Support/AppIdentity.swift b/Sources/Parcel/Support/AppIdentity.swift
new file mode 100644
index 0000000..b036ac0
--- /dev/null
+++ b/Sources/Parcel/Support/AppIdentity.swift
@@ -0,0 +1,55 @@
+import Foundation
+
+/// Central identity constants and one-time migration from the Notable codename.
+enum AppIdentity {
+ static let displayName = "Parcel"
+ static let bundleIdentifier = "dev.parable.Parcel"
+ static let defaultsPrefix = "dev.parable"
+ static let legacyDefaultsPrefix = "io.notable"
+ static let legacyAppSupportComponent = "io.notable.Notable"
+ static let appSupportComponent = "dev.parable.Parcel"
+ static let migrationCompletedKey = "dev.parable.migrationFromNotableCompleted"
+
+ /// Migrates UserDefaults keys and on-disk history from the Notable codename.
+ static func migrateFromNotableIfNeeded() {
+ let defaults = UserDefaults.standard
+ guard !defaults.bool(forKey: migrationCompletedKey) else { return }
+
+ let keyPairs: [(String, String)] = [
+ ("io.notable.hotkey.keyCode", "\(defaultsPrefix).hotkey.keyCode"),
+ ("io.notable.hotkey.modifiers", "\(defaultsPrefix).hotkey.modifiers"),
+ ("io.notable.upload.supabaseURL", "\(defaultsPrefix).upload.supabaseURL"),
+ ("io.notable.upload.anonKey", "\(defaultsPrefix).upload.anonKey"),
+ ("io.notable.upload.bucket", "\(defaultsPrefix).upload.bucket"),
+ ("io.notable.upload.publicBase", "\(defaultsPrefix).upload.publicBase"),
+ ("io.notable.recording.fps", "\(defaultsPrefix).recording.fps"),
+ ("io.notable.brandKits", "\(defaultsPrefix).brandKits"),
+ ("io.notable.lastSaveDirectory", "\(defaultsPrefix).lastSaveDirectory"),
+ ]
+
+ for (oldKey, newKey) in keyPairs {
+ if defaults.object(forKey: newKey) == nil, let value = defaults.object(forKey: oldKey) {
+ defaults.set(value, forKey: newKey)
+ }
+ }
+
+ migrateHistoryDirectoryIfNeeded()
+ defaults.set(true, forKey: migrationCompletedKey)
+ }
+
+ private static func migrateHistoryDirectoryIfNeeded() {
+ let fileManager = FileManager.default
+ guard let appSupport = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else {
+ return
+ }
+ let legacyRoot = appSupport.appendingPathComponent(legacyAppSupportComponent, isDirectory: true)
+ let newRoot = appSupport.appendingPathComponent(appSupportComponent, isDirectory: true)
+ guard fileManager.fileExists(atPath: legacyRoot.path),
+ !fileManager.fileExists(atPath: newRoot.path) else { return }
+ do {
+ try fileManager.moveItem(at: legacyRoot, to: newRoot)
+ } catch {
+ NSLog("Parcel: could not migrate history from Notable β \(error)")
+ }
+ }
+}
diff --git a/Sources/Notable/Support/Extensions.swift b/Sources/Parcel/Support/Extensions.swift
similarity index 97%
rename from Sources/Notable/Support/Extensions.swift
rename to Sources/Parcel/Support/Extensions.swift
index 8b57518..e970c47 100644
--- a/Sources/Notable/Support/Extensions.swift
+++ b/Sources/Parcel/Support/Extensions.swift
@@ -15,6 +15,8 @@ extension CGRect {
}
var area: CGFloat { width * height }
+
+ var center: CGPoint { CGPoint(x: midX, y: midY) }
}
extension CGPoint {
diff --git a/Sources/Parcel/Upload/UploadPreferences.swift b/Sources/Parcel/Upload/UploadPreferences.swift
new file mode 100644
index 0000000..84fd336
--- /dev/null
+++ b/Sources/Parcel/Upload/UploadPreferences.swift
@@ -0,0 +1,34 @@
+import Foundation
+
+/// Supabase Storage configuration. Values are stored in UserDefaults; never commit secrets.
+enum UploadPreferences {
+ private static let urlKey = "\(AppIdentity.defaultsPrefix).upload.supabaseURL"
+ private static let anonKeyKey = "\(AppIdentity.defaultsPrefix).upload.anonKey"
+ private static let bucketKey = "\(AppIdentity.defaultsPrefix).upload.bucket"
+ private static let publicBaseKey = "\(AppIdentity.defaultsPrefix).upload.publicBase"
+
+ static var supabaseURL: String {
+ get { UserDefaults.standard.string(forKey: urlKey) ?? "" }
+ set { UserDefaults.standard.set(newValue.trimmingCharacters(in: .whitespacesAndNewlines), forKey: urlKey) }
+ }
+
+ static var anonKey: String {
+ get { UserDefaults.standard.string(forKey: anonKeyKey) ?? "" }
+ set { UserDefaults.standard.set(newValue.trimmingCharacters(in: .whitespacesAndNewlines), forKey: anonKeyKey) }
+ }
+
+ static var bucketName: String {
+ get { UserDefaults.standard.string(forKey: bucketKey) ?? "captures" }
+ set { UserDefaults.standard.set(newValue.trimmingCharacters(in: .whitespacesAndNewlines), forKey: bucketKey) }
+ }
+
+ /// Optional CDN or custom domain prefix for public object URLs.
+ static var publicBaseURL: String {
+ get { UserDefaults.standard.string(forKey: publicBaseKey) ?? "" }
+ set { UserDefaults.standard.set(newValue.trimmingCharacters(in: .whitespacesAndNewlines), forKey: publicBaseKey) }
+ }
+
+ static var isConfigured: Bool {
+ !supabaseURL.isEmpty && !anonKey.isEmpty && !bucketName.isEmpty
+ }
+}
diff --git a/Sources/Parcel/Upload/UploadService.swift b/Sources/Parcel/Upload/UploadService.swift
new file mode 100644
index 0000000..dc3f0f6
--- /dev/null
+++ b/Sources/Parcel/Upload/UploadService.swift
@@ -0,0 +1,62 @@
+import Foundation
+
+enum UploadError: LocalizedError {
+ case notConfigured
+ case encodeFailed
+ case httpStatus(Int, String)
+ case invalidResponse
+
+ var errorDescription: String? {
+ switch self {
+ case .notConfigured:
+ return "Supabase is not configured. Open Preferences and add your project URL, anon key, and bucket name."
+ case .encodeFailed:
+ return "Could not encode the Capture for upload."
+ case let .httpStatus(code, body):
+ return "Upload failed (HTTP \(code)): \(body)"
+ case .invalidResponse:
+ return "Upload returned an unexpected response."
+ }
+ }
+}
+
+/// Uploads rendered Capture bytes to Supabase Storage via the REST API.
+enum UploadService {
+ static func uploadPNG(data: Data, fileName: String) async throws -> URL {
+ guard UploadPreferences.isConfigured else { throw UploadError.notConfigured }
+
+ let base = UploadPreferences.supabaseURL.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
+ let bucket = UploadPreferences.bucketName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? UploadPreferences.bucketName
+ let objectPath = fileName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? fileName
+ guard let url = URL(string: "\(base)/storage/v1/object/\(bucket)/\(objectPath)") else {
+ throw UploadError.invalidResponse
+ }
+
+ var request = URLRequest(url: url)
+ request.httpMethod = "POST"
+ request.setValue("Bearer \(UploadPreferences.anonKey)", forHTTPHeaderField: "Authorization")
+ request.setValue("image/png", forHTTPHeaderField: "Content-Type")
+ request.setValue("true", forHTTPHeaderField: "x-upsert")
+ request.httpBody = data
+
+ let (responseData, response) = try await URLSession.shared.data(for: request)
+ guard let http = response as? HTTPURLResponse else { throw UploadError.invalidResponse }
+ guard (200...299).contains(http.statusCode) else {
+ let body = String(data: responseData, encoding: .utf8) ?? ""
+ throw UploadError.httpStatus(http.statusCode, body)
+ }
+
+ if !UploadPreferences.publicBaseURL.isEmpty {
+ let publicBase = UploadPreferences.publicBaseURL.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
+ guard let publicURL = URL(string: "\(publicBase)/\(objectPath)") else {
+ throw UploadError.invalidResponse
+ }
+ return publicURL
+ }
+
+ guard let publicURL = URL(string: "\(base)/storage/v1/object/public/\(bucket)/\(objectPath)") else {
+ throw UploadError.invalidResponse
+ }
+ return publicURL
+ }
+}
diff --git a/Sources/Parcel/Vision/TranslationService.swift b/Sources/Parcel/Vision/TranslationService.swift
new file mode 100644
index 0000000..5114431
--- /dev/null
+++ b/Sources/Parcel/Vision/TranslationService.swift
@@ -0,0 +1,46 @@
+import Foundation
+import NaturalLanguage
+
+#if canImport(Translation)
+import Translation
+#endif
+
+enum TranslationService {
+ /// On-device translation via Apple Translation (macOS 26+). Returns nil when unavailable.
+ @MainActor
+ static func translate(_ text: String, to target: Locale.Language = .init(identifier: "en")) async -> String? {
+ guard !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return nil }
+ guard #available(macOS 26.0, *) else { return nil }
+
+ #if canImport(Translation)
+ guard let source = detectLanguage(text) else { return nil }
+ do {
+ let session = TranslationSession(installedSource: source, target: target)
+ try await session.prepareTranslation()
+ let response = try await session.translate(text)
+ return response.targetText
+ } catch {
+ NSLog("Parcel: on-device translation failed β \(error)")
+ return nil
+ }
+ #else
+ return nil
+ #endif
+ }
+
+ static var isAvailable: Bool {
+ if #available(macOS 26.0, *) {
+ #if canImport(Translation)
+ return true
+ #endif
+ }
+ return false
+ }
+
+ private static func detectLanguage(_ text: String) -> Locale.Language? {
+ let recognizer = NLLanguageRecognizer()
+ recognizer.processString(text)
+ guard let language = recognizer.dominantLanguage else { return nil }
+ return Locale.Language(identifier: language.rawValue)
+ }
+}
diff --git a/Sources/Notable/Vision/VisionAnalyzer.swift b/Sources/Parcel/Vision/VisionAnalyzer.swift
similarity index 97%
rename from Sources/Notable/Vision/VisionAnalyzer.swift
rename to Sources/Parcel/Vision/VisionAnalyzer.swift
index cbf2fdc..cb75a87 100644
--- a/Sources/Notable/Vision/VisionAnalyzer.swift
+++ b/Sources/Parcel/Vision/VisionAnalyzer.swift
@@ -55,7 +55,7 @@ enum VisionAnalyzer {
do {
try handler.perform([textRequest, faceRequest, barcodeRequest])
} catch {
- NSLog("Notable: Vision inspection failed β \(error)")
+ NSLog("Parcel: Vision inspection failed β \(error)")
return VisionAnalysis()
}
diff --git a/Website/.gitignore b/Website/.gitignore
new file mode 100644
index 0000000..8b6138c
--- /dev/null
+++ b/Website/.gitignore
@@ -0,0 +1,6 @@
+node_modules
+.next
+out
+.DS_Store
+*.tsbuildinfo
+next-env.d.ts
diff --git a/Website/README.md b/Website/README.md
index 1dc1723..be82f36 100644
--- a/Website/README.md
+++ b/Website/README.md
@@ -1,9 +1,52 @@
-# Notable Website
+# Parcel marketing site β Next.js 16 + React 19 + Tailwind v4 + Motion
-Static marketing/download site for the current local Notable build.
+Same stack as [Parable](https://github.com/bswxyz/parable): Next.js App Router, shadcn-style tokens, and Motion animations.
-Open `index.html` directly in a browser, or serve this folder with any static host. The download
-button points to `downloads/Notable.zip`, which is generated from the current Debug app build.
+## Stack
-Before public distribution, replace the codename, add a final icon, sign and notarize the app, and
-regenerate the zip from a release build.
+| Layer | Choice |
+|-------|--------|
+| Framework | Next.js 16 (static export) |
+| UI | React 19 |
+| Styling | Tailwind CSS v4 |
+| Animation | Motion (`motion/react`) |
+| Fonts | Geist Sans, Geist Mono, Instrument Serif |
+| Theme | next-themes (dark default) |
+
+Visual components (`DitherAurora`, `ShimmerButton`, `VelocityMarquee`) are adapted from the Parable registry.
+
+## Develop
+
+```sh
+cd Website
+npm install
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000).
+
+## Build
+
+```sh
+npm run build
+```
+
+Static output lands in `out/` β deployed to [parcel.parable.dev](https://parcel.parable.dev) via Vercel (root directory: `Website`).
+
+## Deploy (Vercel)
+
+1. Import the repo in Vercel
+2. Set **Root Directory** to `Website`
+3. Framework preset: **Next.js** (auto-detected)
+4. Custom domain: `parcel.parable.dev`
+
+## Public assets
+
+| Path | Purpose |
+|------|---------|
+| `public/downloads/Parcel.zip` | macOS app download |
+| `public/appcast.xml` | Sparkle update feed |
+| `public/og.svg` | Open Graph image |
+| `public/favicon.svg` | Favicon |
+
+Release builds copy `build/Parcel.zip` β `public/downloads/Parcel.zip` via `Scripts/release.sh`.
diff --git a/Website/app/globals.css b/Website/app/globals.css
new file mode 100644
index 0000000..2e43d2a
--- /dev/null
+++ b/Website/app/globals.css
@@ -0,0 +1,96 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+ --font-display: var(--font-instrument-serif);
+ --color-card: var(--card);
+ --color-card-foreground: var(--card-foreground);
+ --color-muted: var(--muted);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-border: var(--border);
+ --color-ring: var(--ring);
+ --color-primary: var(--primary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-secondary: var(--secondary);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-accent: var(--accent);
+ --color-accent-foreground: var(--accent-foreground);
+ --radius-sm: calc(var(--radius) * 0.6);
+ --radius-md: calc(var(--radius) * 0.8);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) * 1.4);
+ --radius-2xl: calc(var(--radius) * 1.8);
+}
+
+:root {
+ --pb-violet: #8b5cf6;
+ --pb-fuchsia: #ec4899;
+ --pb-mint: #5ee4b5;
+ --pb-gold: #f5c451;
+ --pb-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
+ --pb-ease-snap: cubic-bezier(0.16, 1, 0.3, 1);
+
+ --background: oklch(0.985 0 0);
+ --foreground: oklch(0.145 0 0);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0 0);
+ --primary: oklch(0.205 0 0);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.97 0 0);
+ --secondary-foreground: oklch(0.205 0 0);
+ --muted: oklch(0.97 0 0);
+ --muted-foreground: oklch(0.556 0 0);
+ --accent: oklch(0.97 0 0);
+ --accent-foreground: oklch(0.205 0 0);
+ --border: oklch(0.922 0 0);
+ --ring: oklch(0.708 0 0);
+ --radius: 0.625rem;
+}
+
+.dark {
+ --background: oklch(0.12 0 0);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.18 0 0);
+ --card-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.922 0 0);
+ --primary-foreground: oklch(0.205 0 0);
+ --secondary: oklch(0.24 0 0);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.24 0 0);
+ --muted-foreground: oklch(0.708 0 0);
+ --accent: oklch(0.24 0 0);
+ --accent-foreground: oklch(0.985 0 0);
+ --border: oklch(1 0 0 / 10%);
+ --ring: oklch(0.556 0 0);
+}
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+ html {
+ @apply scroll-smooth font-sans antialiased;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html {
+ scroll-behavior: auto;
+ }
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/Website/app/layout.tsx b/Website/app/layout.tsx
new file mode 100644
index 0000000..e9b6793
--- /dev/null
+++ b/Website/app/layout.tsx
@@ -0,0 +1,72 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono, Instrument_Serif } from "next/font/google";
+import "./globals.css";
+import { ThemeProvider } from "@/components/theme-provider";
+import { SiteNav } from "@/components/site-nav";
+import { SiteFooter } from "@/components/site-footer";
+import { SITE_URL } from "@/lib/site";
+
+const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"] });
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+const instrumentSerif = Instrument_Serif({
+ variable: "--font-instrument-serif",
+ subsets: ["latin"],
+ weight: "400",
+ style: "italic",
+});
+
+export const metadata: Metadata = {
+ title: {
+ default: "Parcel β native macOS Capture studio",
+ template: "%s Β· Parcel",
+ },
+ description:
+ "Freeze your screen, annotate with fourteen tools, censor with on-device Vision, beautify, record, and upload β from Parable. No cloud AI, ever.",
+ metadataBase: new URL(SITE_URL),
+ openGraph: {
+ title: "Parcel β native macOS Capture studio",
+ description:
+ "The Capture studio Apple forgot to ship. MIT licensed, on-device only.",
+ url: SITE_URL,
+ siteName: "Parcel",
+ images: [{ url: "/og.svg", width: 1200, height: 630 }],
+ locale: "en_US",
+ type: "website",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "Parcel β native macOS Capture studio",
+ description:
+ "Freeze, annotate, censor, beautify, record β no cloud AI, ever.",
+ images: ["/og.svg"],
+ },
+ icons: { icon: "/favicon.svg" },
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{ children: React.ReactNode }>) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/Website/app/page.tsx b/Website/app/page.tsx
new file mode 100644
index 0000000..5ce5a00
--- /dev/null
+++ b/Website/app/page.tsx
@@ -0,0 +1,29 @@
+import type { Metadata } from "next";
+import { HomeHero } from "@/components/home-hero";
+import { FeatureBento } from "@/components/feature-bento";
+import { WorkflowSteps } from "@/components/workflow-steps";
+import { PrivacySection } from "@/components/privacy-section";
+import { ShortcutsTable } from "@/components/shortcuts-table";
+import { GuidesGrid } from "@/components/guides-grid";
+import { InstallSection } from "@/components/install-section";
+import { FaqSection, CtaBanner } from "@/components/faq-section";
+
+export const metadata: Metadata = {
+ alternates: { canonical: "/" },
+};
+
+export default function HomePage() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/Website/assets/notable-permission.png b/Website/assets/notable-permission.png
deleted file mode 100644
index 06d3560..0000000
Binary files a/Website/assets/notable-permission.png and /dev/null differ
diff --git a/Website/components/copy-button.tsx b/Website/components/copy-button.tsx
new file mode 100644
index 0000000..07734df
--- /dev/null
+++ b/Website/components/copy-button.tsx
@@ -0,0 +1,45 @@
+"use client";
+
+import * as React from "react";
+import { Check, Copy } from "lucide-react";
+import { cn } from "@/lib/utils";
+
+export function CopyButton({
+ value,
+ label = "Copy",
+ className,
+}: {
+ value: string;
+ label?: string;
+ className?: string;
+}) {
+ const [copied, setCopied] = React.useState(false);
+
+ async function onCopy() {
+ try {
+ await navigator.clipboard.writeText(value);
+ } catch {
+ return;
+ }
+ setCopied(true);
+ window.setTimeout(() => setCopied(false), 1600);
+ }
+
+ return (
+
+ {copied ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/Website/components/editor-preview.tsx b/Website/components/editor-preview.tsx
new file mode 100644
index 0000000..de2fb03
--- /dev/null
+++ b/Website/components/editor-preview.tsx
@@ -0,0 +1,211 @@
+"use client";
+
+import * as React from "react";
+import {
+ ChevronLeft,
+ ChevronRight,
+ EyeOff,
+ Lock,
+ MousePointer2,
+ MoveUpRight,
+ RotateCw,
+ Search,
+ Sparkles,
+ Square,
+ Type,
+} from "lucide-react";
+import { cn } from "@/lib/utils";
+
+const BROWSER_DROP =
+ "0 1px 2px rgba(0,0,0,0.08), 0 12px 28px -10px rgba(0,0,0,0.28), 0 34px 64px -28px rgba(0,0,0,0.34)";
+
+function TrafficLights() {
+ return (
+
+ {["#ff5f57", "#febc2e", "#28c840"].map((c) => (
+
+ ))}
+
+ );
+}
+
+function UrlBar({ url }: { url: string }) {
+ return (
+
+
+ {url}
+
+
+ );
+}
+
+export function DeviceFrame({
+ url = "parcel.parable.dev/editor",
+ tab = "Parcel β Editor",
+ children,
+ className,
+}: {
+ url?: string;
+ tab?: string;
+ children?: React.ReactNode;
+ className?: string;
+}) {
+ return (
+
+ );
+}
+
+const TOOL_CHIPS = [
+ { label: "Select", icon: MousePointer2 },
+ { label: "Arrow", icon: MoveUpRight },
+ { label: "Rect", icon: Square },
+ { label: "Text", icon: Type },
+ { label: "Censor", icon: EyeOff },
+ { label: "Beautify", icon: Sparkles },
+ { label: "Loupe", icon: Search },
+] as const;
+
+export function EditorPreview() {
+ const [activeTool, setActiveTool] = React.useState(0);
+
+ React.useEffect(() => {
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
+ if (reduce) return;
+ const id = window.setInterval(
+ () => setActiveTool((i) => (i + 1) % TOOL_CHIPS.length),
+ 2400
+ );
+ return () => window.clearInterval(id);
+ }, []);
+
+ return (
+
+
+ {TOOL_CHIPS.map((t, i) => {
+ const Icon = t.icon;
+ const active = i === activeTool;
+ return (
+
+
+ {t.label}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ REDACTED
+
+
+
+ Capture coords Β· 2Γ retina
+
+ On screen = saved
+
+
+
+
+
+
+ );
+}
diff --git a/Website/components/faq-section.tsx b/Website/components/faq-section.tsx
new file mode 100644
index 0000000..c6dd634
--- /dev/null
+++ b/Website/components/faq-section.tsx
@@ -0,0 +1,87 @@
+"use client";
+
+import * as React from "react";
+import { ChevronDown } from "lucide-react";
+import { AnimatePresence, motion, useReducedMotion } from "motion/react";
+import { SectionHeader } from "@/components/section-header";
+import { ShimmerButton } from "@/components/parable/shimmer-button";
+import { DOWNLOAD_URL, faqs } from "@/lib/site";
+import { cn } from "@/lib/utils";
+
+export function FaqSection() {
+ const [open, setOpen] = React.useState(0);
+ const reduce = useReducedMotion();
+
+ return (
+
+
+
+
+ {faqs.map((item, i) => {
+ const isOpen = open === i;
+ return (
+
+ setOpen(isOpen ? null : i)}
+ aria-expanded={isOpen}
+ className="flex w-full items-center justify-between gap-4 px-5 py-4 text-left transition-colors hover:bg-muted/30"
+ >
+ {item.q}
+
+
+
+ {isOpen && (
+
+
+ {item.a}
+
+
+ )}
+
+
+ );
+ })}
+
+
+
+ );
+}
+
+export function CtaBanner() {
+ return (
+
+
+
+
+ Ready to Capture?
+
+
+ Free, open source, and built for daily macOS work β private by default.
+
+
+ Download Parcel
+
+
+
+ );
+}
diff --git a/Website/components/feature-bento.tsx b/Website/components/feature-bento.tsx
new file mode 100644
index 0000000..fce1b41
--- /dev/null
+++ b/Website/components/feature-bento.tsx
@@ -0,0 +1,127 @@
+"use client";
+
+import * as React from "react";
+import { motion, useReducedMotion } from "motion/react";
+import {
+ Camera,
+ CloudUpload,
+ Eye,
+ History,
+ PenTool,
+ ScrollText,
+ Sparkles,
+ Video,
+ type LucideIcon,
+} from "lucide-react";
+import { cn } from "@/lib/utils";
+import { SectionHeader } from "@/components/section-header";
+import { features } from "@/lib/site";
+
+const ICONS: Record = {
+ camera: Camera,
+ pen: PenTool,
+ scroll: ScrollText,
+ video: Video,
+ sparkles: Sparkles,
+ eye: Eye,
+ history: History,
+ cloud: CloudUpload,
+};
+
+function hexToRgba(hex: string, alpha: number) {
+ const n = Number.parseInt(hex.replace("#", ""), 16);
+ return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;
+}
+
+export function FeatureBento() {
+ const reduce = useReducedMotion();
+ const accent = "#8b5cf6";
+
+ const onGlowMove = React.useCallback(
+ (e: React.PointerEvent) => {
+ const el = e.currentTarget;
+ const rect = el.getBoundingClientRect();
+ el.style.setProperty("--mx", `${Math.round(e.clientX - rect.left)}px`);
+ el.style.setProperty("--my", `${Math.round(e.clientY - rect.top)}px`);
+ },
+ []
+ );
+
+ return (
+
+
+
+
+ {features.map((item, i) => {
+ const size = item.size ?? "sm";
+ const Icon = item.icon ? ICONS[item.icon] : null;
+ const spansWide = size === "wide";
+ const spansTall = size === "tall";
+
+ return (
+
+
+
+ {Icon && (
+
+
+
+
+
+
+ )}
+
+ {item.title}
+
+
+ {item.body}
+
+
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/Website/components/guides-grid.tsx b/Website/components/guides-grid.tsx
new file mode 100644
index 0000000..8505873
--- /dev/null
+++ b/Website/components/guides-grid.tsx
@@ -0,0 +1,44 @@
+"use client";
+
+import { BookOpen } from "lucide-react";
+import { motion, useReducedMotion } from "motion/react";
+import { SectionHeader } from "@/components/section-header";
+import { guides } from "@/lib/site";
+
+export function GuidesGrid() {
+ const reduce = useReducedMotion();
+
+ return (
+
+
+
+
+ {guides.map((guide, i) => (
+
+
+ {guide.title}
+
+ {guide.body}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/Website/components/home-hero.tsx b/Website/components/home-hero.tsx
new file mode 100644
index 0000000..1cd884a
--- /dev/null
+++ b/Website/components/home-hero.tsx
@@ -0,0 +1,162 @@
+"use client";
+
+import Link from "next/link";
+import { ArrowRight } from "lucide-react";
+import { motion, useReducedMotion } from "motion/react";
+import { DitherAurora } from "@/components/parable/dither-aurora";
+import { ShimmerButton } from "@/components/parable/shimmer-button";
+import { VelocityMarquee } from "@/components/parable/velocity-marquee";
+import { DeviceFrame, EditorPreview } from "@/components/editor-preview";
+import { Badge } from "@/components/section-header";
+import { DOWNLOAD_URL, logos, stats } from "@/lib/site";
+
+const marqueeItems = logos.map((name) => (
+
+ {name}
+ /
+
+));
+
+const fadeUp = {
+ hidden: { opacity: 0, y: 18 },
+ show: (i: number) => ({
+ opacity: 1,
+ y: 0,
+ transition: {
+ type: "spring" as const,
+ stiffness: 220,
+ damping: 26,
+ delay: i * 0.08,
+ },
+ }),
+};
+
+export function HomeHero() {
+ const reduce = useReducedMotion();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Native macOS Β· MIT Β· Open source
+
+
+ The{" "}
+
+ Capture
+ {" "}
+ studio Apple forgot to{" "}
+
+ ship
+
+ .
+
+
+ Freeze your screen, annotate with fourteen tools, censor with local
+ Vision, beautify for ship-ready output, record, scroll-capture, and
+ upload when you choose β from the Parable ecosystem.
+
+
+ macOS 13+
+ On-device only
+ MIT License
+
+
+
+ Download for macOS
+
+
+ See how it works
+
+
+
+ {stats.map((s) => (
+
+
+ {s.value}
+
+ {s.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/Website/components/install-section.tsx b/Website/components/install-section.tsx
new file mode 100644
index 0000000..2d5bc5f
--- /dev/null
+++ b/Website/components/install-section.tsx
@@ -0,0 +1,57 @@
+"use client";
+
+import { Download } from "lucide-react";
+import { CopyButton } from "@/components/copy-button";
+import { SectionHeader } from "@/components/section-header";
+import { ShimmerButton } from "@/components/parable/shimmer-button";
+import { DOWNLOAD_URL, HOMEBREW_CMD } from "@/lib/site";
+
+export function InstallSection() {
+ return (
+
+
+
+
+
+
+
+
+
Direct download
+
+ Signed Release builds from GitHub Actions. Unzip and drag Parcel to
+ Applications.
+
+
+ Download Parcel.zip
+
+
+
+
+
+ Homebrew
+
+
Install via cask
+
+ Once the cask is published to a tap, install with Homebrew.
+
+
+
+ {HOMEBREW_CMD}
+
+
+
+
+
+
+
+ );
+}
diff --git a/Website/components/parable/dither-aurora.tsx b/Website/components/parable/dither-aurora.tsx
new file mode 100644
index 0000000..8c4b151
--- /dev/null
+++ b/Website/components/parable/dither-aurora.tsx
@@ -0,0 +1,396 @@
+"use client";
+
+import * as React from "react";
+import { cn } from "@/lib/utils";
+
+/** Parse "#rgb" / "#rrggbb" into a normalised [r, g, b] triple. */
+function hexToRgb(hex: string): [number, number, number] {
+ let h = hex.replace("#", "").trim();
+ if (h.length === 3)
+ h = h
+ .split("")
+ .map((c) => c + c)
+ .join("");
+ const n = Number.parseInt(h, 16);
+ if (Number.isNaN(n)) return [0, 0, 0];
+ return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];
+}
+
+function usePrefersReducedMotion(): boolean {
+ const [reduced, setReduced] = React.useState(false);
+ React.useEffect(() => {
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
+ setReduced(mq.matches);
+ const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);
+ mq.addEventListener("change", onChange);
+ return () => mq.removeEventListener("change", onChange);
+ }, []);
+ return reduced;
+}
+
+const VERT = `
+attribute vec2 a_pos;
+void main() {
+ gl_Position = vec4(a_pos, 0.0, 1.0);
+}
+`;
+
+/**
+ * Two-octave value noise, domain-warped into a slow curl-like flow, mapped
+ * onto a three-stop gradient, then ordered-dithered: each channel is
+ * quantised to six levels and a 4x4 Bayer offset rounds the low bits so the
+ * field renders as a visible pixel lattice instead of banding.
+ */
+const FRAG = `
+#ifdef GL_FRAGMENT_PRECISION_HIGH
+precision highp float;
+#else
+precision mediump float;
+#endif
+
+uniform vec2 u_res;
+uniform float u_time;
+uniform vec2 u_par;
+uniform vec3 u_c0;
+uniform vec3 u_c1;
+uniform vec3 u_c2;
+uniform vec3 u_bg;
+uniform float u_px;
+uniform float u_grain;
+
+float hash21(vec2 p) {
+ p = fract(p * vec2(234.34, 435.345));
+ p += dot(p, p + 34.23);
+ return fract(p.x * p.y);
+}
+
+float vnoise(vec2 p) {
+ vec2 i = floor(p);
+ vec2 f = fract(p);
+ vec2 u = f * f * (3.0 - 2.0 * f);
+ return mix(
+ mix(hash21(i), hash21(i + vec2(1.0, 0.0)), u.x),
+ mix(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(1.0, 1.0)), u.x),
+ u.y
+ );
+}
+
+float fbm(vec2 p) {
+ return 0.625 * vnoise(p) + 0.375 * vnoise(p * 2.13 + 17.7);
+}
+
+/* 2x2 Bayer index: [[0,2],[3,1]] via 2*(x xor y) + y. */
+float bayer2(vec2 p) {
+ return 2.0 * mod(p.x + p.y, 2.0) + p.y;
+}
+
+/* Recursive 4x4 Bayer, normalised to (0, 1). */
+float bayer4(vec2 p) {
+ vec2 q = floor(mod(p, 4.0));
+ return (4.0 * bayer2(mod(q, 2.0)) + bayer2(floor(q * 0.5)) + 0.5) / 16.0;
+}
+
+void main() {
+ float px = max(u_px, 1.0);
+ vec2 cell = floor(gl_FragCoord.xy / px);
+ vec2 sp = (cell + 0.5) * px / u_res;
+ vec2 uv = vec2(sp.x * u_res.x / u_res.y, sp.y) + u_par;
+
+ float t = u_time;
+ vec2 drift = vec2(t * 0.35, -t * 0.22);
+
+ vec2 q = vec2(
+ fbm(uv * 1.6 + drift),
+ fbm(uv * 1.6 - drift.yx + 4.2)
+ );
+ float n = fbm(uv * 2.1 + (q - 0.5) * 1.5 + vec2(-t * 0.28, t * 0.19));
+
+ vec2 v = sp - 0.5;
+ n *= 1.0 - dot(v, v) * 0.55;
+
+ float m = smoothstep(0.34, 0.8, n);
+ vec3 aurora = mix(u_c0, u_c1, smoothstep(0.1, 0.62, m));
+ aurora = mix(aurora, u_c2, smoothstep(0.62, 0.96, m));
+ vec3 col = mix(u_bg, aurora, m * 0.92);
+
+ float d = bayer4(cell) - 0.5;
+ col = clamp(col, 0.0, 1.0);
+ col = floor(col * 5.0 + 0.5 + d) / 5.0;
+
+ col += (hash21(gl_FragCoord.xy + fract(t) * 61.7) - 0.5) * 0.05 * u_grain;
+
+ gl_FragColor = vec4(col, 1.0);
+}
+`;
+
+export interface DitherAuroraProps
+ extends React.HTMLAttributes {
+ /** Two or three hex colours the aurora gradient sweeps through. */
+ colors?: string[];
+ /** Base backdrop colour (also the GL clear colour). */
+ background?: string;
+ /** Flow speed multiplier. ~0.15 is a slow, hero-grade drift. */
+ speed?: number;
+ /** Dither cell size in device pixels β the size of one lattice dot. */
+ pixelSize?: number;
+ /** Freeze the flow (rendering idles; the last frame stays up). */
+ paused?: boolean;
+ /** Add a subtle animated film-grain pass on top of the lattice. */
+ grain?: boolean;
+}
+
+/**
+ * DitherAurora β "Prism Drift", Parable's signature hero. A WebGL
+ * ordered-dither aurora: two-octave value noise is domain-warped into a slow
+ * curl-like flow, mapped onto a three-stop gradient, then quantised through a
+ * 4x4 Bayer matrix so it renders as a visible pixel lattice β craft, not
+ * banding. Pointer parallax is spring-smoothed (~2.5% drift); the single rAF
+ * loop pauses off-screen via IntersectionObserver, devicePixelRatio is capped
+ * at 2, and WebGL context loss is survived. Under prefers-reduced-motion it
+ * renders exactly one static frame. Default palette (violet / fuchsia / ember
+ * on ink) mirrors the site's --pb-* tokens. Content renders above via a
+ * relative z-10 wrapper, same API shape as AuroraBackground.
+ *
+ * @parable/dither-aurora
+ */
+export function DitherAurora({
+ colors = ["#8b5cf6", "#ec4899", "#f5a623"],
+ background = "#0f0f10",
+ speed = 0.15,
+ pixelSize = 4,
+ paused = false,
+ grain = false,
+ className,
+ children,
+ style,
+ ...props
+}: DitherAuroraProps) {
+ const rootRef = React.useRef(null);
+ const canvasRef = React.useRef(null);
+ const reduced = usePrefersReducedMotion();
+
+ // Live-updatable knobs that must not tear down the GL context.
+ const live = React.useRef({ speed, paused });
+ live.current.speed = speed;
+ live.current.paused = paused;
+
+ const colorKey = `${colors.join("|")}|${background}`;
+
+ React.useEffect(() => {
+ const root = rootRef.current;
+ const canvas = canvasRef.current;
+ if (!root || !canvas) return;
+
+ const gl = canvas.getContext("webgl", {
+ alpha: false,
+ antialias: false,
+ depth: false,
+ stencil: false,
+ powerPreference: "low-power",
+ });
+ if (!gl) return;
+
+ const stops = colors.filter(Boolean).map(hexToRgb);
+ const c0 = stops[0] ?? hexToRgb("#8b5cf6");
+ const c1 = stops[1] ?? c0;
+ const c2 = stops[2] ?? c1;
+ const bg = hexToRgb(background);
+
+ let program: WebGLProgram | null = null;
+ let buffer: WebGLBuffer | null = null;
+ let uTime: WebGLUniformLocation | null = null;
+ let uRes: WebGLUniformLocation | null = null;
+ let uPar: WebGLUniformLocation | null = null;
+
+ const compile = (type: number, src: string): WebGLShader | null => {
+ const sh = gl.createShader(type);
+ if (!sh) return null;
+ gl.shaderSource(sh, src);
+ gl.compileShader(sh);
+ if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
+ gl.deleteShader(sh);
+ return null;
+ }
+ return sh;
+ };
+
+ const setup = (): boolean => {
+ const vs = compile(gl.VERTEX_SHADER, VERT);
+ const fs = compile(gl.FRAGMENT_SHADER, FRAG);
+ if (!vs || !fs) return false;
+ const prog = gl.createProgram();
+ if (!prog) return false;
+ gl.attachShader(prog, vs);
+ gl.attachShader(prog, fs);
+ gl.linkProgram(prog);
+ gl.deleteShader(vs);
+ gl.deleteShader(fs);
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
+ gl.deleteProgram(prog);
+ return false;
+ }
+ program = prog;
+ gl.useProgram(prog);
+
+ buffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ new Float32Array([-1, -1, 3, -1, -1, 3]),
+ gl.STATIC_DRAW
+ );
+ const aPos = gl.getAttribLocation(prog, "a_pos");
+ gl.enableVertexAttribArray(aPos);
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
+
+ uTime = gl.getUniformLocation(prog, "u_time");
+ uRes = gl.getUniformLocation(prog, "u_res");
+ uPar = gl.getUniformLocation(prog, "u_par");
+ gl.uniform3f(gl.getUniformLocation(prog, "u_c0"), c0[0], c0[1], c0[2]);
+ gl.uniform3f(gl.getUniformLocation(prog, "u_c1"), c1[0], c1[1], c1[2]);
+ gl.uniform3f(gl.getUniformLocation(prog, "u_c2"), c2[0], c2[1], c2[2]);
+ gl.uniform3f(gl.getUniformLocation(prog, "u_bg"), bg[0], bg[1], bg[2]);
+ gl.uniform1f(
+ gl.getUniformLocation(prog, "u_px"),
+ Math.max(1, pixelSize)
+ );
+ gl.uniform1f(gl.getUniformLocation(prog, "u_grain"), grain ? 1 : 0);
+ return true;
+ };
+
+ let raf = 0;
+ let lost = false;
+ let visible = true;
+ let time = Math.random() * 40;
+ let last = performance.now();
+ const STATIC_T = 6.4;
+
+ // Pointer parallax, integrated as a spring in the render loop.
+ const par = { x: 0, y: 0, vx: 0, vy: 0, tx: 0, ty: 0 };
+ const STIFFNESS = 200;
+ const DAMPING = 22;
+
+ const draw = (t: number, ox: number, oy: number) => {
+ if (!program || lost) return;
+ gl.uniform1f(uTime, t);
+ gl.uniform2f(uRes, canvas.width, canvas.height);
+ gl.uniform2f(uPar, ox, oy);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ };
+
+ const resize = () => {
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
+ const w = Math.max(1, Math.round(root.clientWidth * dpr));
+ const h = Math.max(1, Math.round(root.clientHeight * dpr));
+ if (canvas.width !== w || canvas.height !== h) {
+ canvas.width = w;
+ canvas.height = h;
+ gl.viewport(0, 0, w, h);
+ draw(reduced ? STATIC_T : time, par.x, par.y);
+ }
+ };
+
+ const frame = (now: number) => {
+ raf = requestAnimationFrame(frame);
+ const dt = Math.min((now - last) / 1000, 0.05);
+ last = now;
+ if (lost || !visible || live.current.paused) return;
+
+ time += dt * live.current.speed;
+
+ par.vx += (par.tx - par.x) * STIFFNESS * dt;
+ par.vy += (par.ty - par.y) * STIFFNESS * dt;
+ const decay = Math.exp(-DAMPING * dt);
+ par.vx *= decay;
+ par.vy *= decay;
+ par.x += par.vx * dt;
+ par.y += par.vy * dt;
+
+ draw(time, par.x, par.y);
+ };
+
+ const onPointerMove = (e: PointerEvent) => {
+ const r = root.getBoundingClientRect();
+ if (!r.width || !r.height) return;
+ par.tx = ((e.clientX - r.left) / r.width - 0.5) * 0.05;
+ par.ty = (0.5 - (e.clientY - r.top) / r.height) * 0.05;
+ };
+ const onPointerLeave = () => {
+ par.tx = 0;
+ par.ty = 0;
+ };
+
+ const onLost = (e: Event) => {
+ e.preventDefault();
+ lost = true;
+ program = null;
+ buffer = null;
+ };
+ const onRestored = () => {
+ lost = false;
+ if (setup()) {
+ gl.viewport(0, 0, canvas.width, canvas.height);
+ draw(reduced ? STATIC_T : time, par.x, par.y);
+ }
+ };
+ canvas.addEventListener("webglcontextlost", onLost);
+ canvas.addEventListener("webglcontextrestored", onRestored);
+
+ const io = new IntersectionObserver((entries) => {
+ visible = entries[0]?.isIntersecting ?? true;
+ });
+ io.observe(root);
+
+ const ro = new ResizeObserver(resize);
+ ro.observe(root);
+
+ const ok = setup();
+ if (ok) {
+ resize();
+ if (reduced) {
+ draw(STATIC_T, 0, 0);
+ } else {
+ draw(time, 0, 0);
+ root.addEventListener("pointermove", onPointerMove);
+ root.addEventListener("pointerleave", onPointerLeave);
+ last = performance.now();
+ raf = requestAnimationFrame(frame);
+ }
+ }
+
+ return () => {
+ cancelAnimationFrame(raf);
+ io.disconnect();
+ ro.disconnect();
+ root.removeEventListener("pointermove", onPointerMove);
+ root.removeEventListener("pointerleave", onPointerLeave);
+ canvas.removeEventListener("webglcontextlost", onLost);
+ canvas.removeEventListener("webglcontextrestored", onRestored);
+ if (buffer) gl.deleteBuffer(buffer);
+ if (program) gl.deleteProgram(program);
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
+ };
+ // colorKey covers `colors` and `background`; speed/paused apply via ref.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [colorKey, pixelSize, grain, reduced]);
+
+ return (
+
+ {/* Keyed so prop changes remount the canvas with a fresh GL context. */}
+
+
{children}
+
+ );
+}
+
+export default DitherAurora;
diff --git a/Website/components/parable/shimmer-button.tsx b/Website/components/parable/shimmer-button.tsx
new file mode 100644
index 0000000..9232fab
--- /dev/null
+++ b/Website/components/parable/shimmer-button.tsx
@@ -0,0 +1,114 @@
+"use client";
+
+import * as React from "react";
+import { cn } from "@/lib/utils";
+
+function useInjectedKeyframes(id: string, css: string) {
+ React.useEffect(() => {
+ if (typeof document === "undefined" || document.getElementById(id)) return;
+ const el = document.createElement("style");
+ el.id = id;
+ el.textContent = css;
+ document.head.appendChild(el);
+ }, [id, css]);
+}
+
+export interface ShimmerButtonProps
+ extends React.ButtonHTMLAttributes {
+ /** Colour of the travelling shimmer highlight. */
+ shimmerColor?: string;
+ /** Diameter of the shimmer highlight. */
+ shimmerSize?: string;
+ /** Seconds for one full rotation. */
+ shimmerDuration?: string;
+ /** Corner radius. */
+ radius?: string;
+ /** Background of the button face. */
+ background?: string;
+ /**
+ * Render as a different element for navigation semantics β e.g. an `` or a
+ * router `Link`. Defaults to `"button"`. Pass `href` alongside for links.
+ */
+ as?: React.ElementType;
+ /** Destination, used when rendering as a link element via `as`. */
+ href?: string;
+}
+
+/**
+ * ShimmerButton β a pill button with a conic-gradient highlight that travels
+ * around the border. Fully self-contained (ships its own keyframes), themeable
+ * via props, and disables motion under `prefers-reduced-motion`.
+ *
+ * @parable/shimmer-button
+ */
+export const ShimmerButton = React.forwardRef<
+ HTMLButtonElement,
+ ShimmerButtonProps
+>(function ShimmerButton(
+ {
+ className,
+ children,
+ shimmerColor = "#c4b5fd",
+ shimmerSize = "0.06em",
+ shimmerDuration = "2.4s",
+ radius = "9999px",
+ background = "rgba(10,10,12,1)",
+ style,
+ as,
+ ...props
+ },
+ ref
+) {
+ const Comp = (as ?? "button") as React.ElementType;
+ useInjectedKeyframes(
+ "pb-shimmer-button-kf",
+ "@keyframes pb-slide{to{transform:translate(-50%,-50%) rotate(360deg)}}.pb-shimmer{transform-origin:center}@media (prefers-reduced-motion:reduce){.pb-shimmer{animation:none!important}}"
+ );
+
+ const vars = {
+ "--spread": "90deg",
+ "--shimmer-color": shimmerColor,
+ "--radius": radius,
+ "--speed": shimmerDuration,
+ "--cut": shimmerSize,
+ "--bg": background,
+ } as React.CSSProperties;
+
+ return (
+
+ {/* travelling shimmer */}
+
+
+
+
+
+
+ {/* content */}
+ {children}
+
+ {/* highlight + inner face mask */}
+
+
+ );
+});
+
+export default ShimmerButton;
diff --git a/Website/components/parable/velocity-marquee.tsx b/Website/components/parable/velocity-marquee.tsx
new file mode 100644
index 0000000..7d8a6ba
--- /dev/null
+++ b/Website/components/parable/velocity-marquee.tsx
@@ -0,0 +1,109 @@
+"use client";
+
+import * as React from "react";
+import { cn } from "@/lib/utils";
+
+export interface VelocityMarqueeProps
+ extends React.HTMLAttributes {
+ /** Items to scroll. Strings or nodes. */
+ items: React.ReactNode[];
+ /** Base pixels/second. */
+ baseSpeed?: number;
+ /** Scroll direction. */
+ direction?: "left" | "right";
+ /** Extra px/sec added at peak scroll velocity. */
+ scrollBoost?: number;
+ /** Gap between items, any CSS length. */
+ gap?: string;
+}
+
+/**
+ * VelocityMarquee β an infinite marquee that speeds up with the page's scroll
+ * velocity and eases back to its base speed. rAF-driven, seamless (two tracks),
+ * and frozen under prefers-reduced-motion.
+ *
+ * @parable/velocity-marquee
+ */
+export function VelocityMarquee({
+ items,
+ baseSpeed = 60,
+ direction = "left",
+ scrollBoost = 340,
+ gap = "3rem",
+ className,
+ ...props
+}: VelocityMarqueeProps) {
+ const trackRef = React.useRef(null);
+ const offset = React.useRef(0);
+ const boost = React.useRef(0);
+ const lastScroll = React.useRef(0);
+ const lastT = React.useRef(0);
+ const width = React.useRef(0);
+
+ React.useEffect(() => {
+ const track = trackRef.current;
+ if (!track) return;
+ const reduce = window.matchMedia(
+ "(prefers-reduced-motion: reduce)"
+ ).matches;
+ const measure = () => (width.current = track.scrollWidth / 2);
+ measure();
+ const ro = new ResizeObserver(measure);
+ ro.observe(track);
+
+ const onScroll = () => {
+ const y = window.scrollY;
+ boost.current = Math.min(1, Math.abs(y - lastScroll.current) / 40);
+ lastScroll.current = y;
+ };
+ window.addEventListener("scroll", onScroll, { passive: true });
+
+ let raf = 0;
+ const dir = direction === "left" ? -1 : 1;
+ const tick = (t: number) => {
+ const dt = lastT.current ? (t - lastT.current) / 1000 : 0;
+ lastT.current = t;
+ const speed = baseSpeed + boost.current * scrollBoost;
+ boost.current *= 0.9; // decay back to base
+ if (!reduce) offset.current += dir * speed * dt;
+ const w = width.current || 1;
+ offset.current = ((offset.current % w) + w) % w;
+ track.style.transform = `translate3d(${
+ dir < 0 ? -offset.current : offset.current - w
+ }px,0,0)`;
+ raf = requestAnimationFrame(tick);
+ };
+ raf = requestAnimationFrame(tick);
+ return () => {
+ cancelAnimationFrame(raf);
+ ro.disconnect();
+ window.removeEventListener("scroll", onScroll);
+ };
+ }, [baseSpeed, direction, scrollBoost]);
+
+ const row = (key: string) =>
+ items.map((it, i) => (
+
+ {it}
+
+ ));
+
+ return (
+
+
+ {row("a")}
+ {row("b")}
+
+
+ );
+}
+
+export default VelocityMarquee;
diff --git a/Website/components/privacy-section.tsx b/Website/components/privacy-section.tsx
new file mode 100644
index 0000000..b212899
--- /dev/null
+++ b/Website/components/privacy-section.tsx
@@ -0,0 +1,79 @@
+"use client";
+
+import { Shield } from "lucide-react";
+import { motion, useReducedMotion } from "motion/react";
+import { SectionHeader } from "@/components/section-header";
+
+export function PrivacySection() {
+ const reduce = useReducedMotion();
+
+ return (
+
+
+
+
+
+
+
+
+ Vision and Core ML process Captures locally
+
+
+
+ Supabase upload is optional and user-configured
+
+
+
+ Sandboxed Release builds with minimal entitlements
+
+
+
+
+
+
+
+ System Settings β Privacy & Security
+
+
+
+
+
+
Screen Recording
+
Required for Capture
+
+
+ Granted
+
+
+
+
+
Accessibility
+
Not required
+
+
β
+
+
+ Carbon hotkeys Β· no Accessibility permission needed
+
+
+
+
+
+
+ );
+}
diff --git a/Website/components/section-header.tsx b/Website/components/section-header.tsx
new file mode 100644
index 0000000..d9fe7ec
--- /dev/null
+++ b/Website/components/section-header.tsx
@@ -0,0 +1,54 @@
+import { cn } from "@/lib/utils";
+
+export function SectionHeader({
+ kicker,
+ title,
+ subtitle,
+ className,
+}: {
+ kicker?: string;
+ title: string;
+ subtitle?: string;
+ className?: string;
+}) {
+ return (
+
+ {kicker && (
+
+ {kicker}
+
+ )}
+
+ {title}
+
+ {subtitle && (
+
+ {subtitle}
+
+ )}
+
+ );
+}
+
+export function Badge({
+ children,
+ variant = "default",
+}: {
+ children: React.ReactNode;
+ variant?: "default" | "violet" | "mint";
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/Website/components/shortcuts-table.tsx b/Website/components/shortcuts-table.tsx
new file mode 100644
index 0000000..6eae4e7
--- /dev/null
+++ b/Website/components/shortcuts-table.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import { motion, useReducedMotion } from "motion/react";
+import { SectionHeader } from "@/components/section-header";
+import { shortcuts } from "@/lib/site";
+
+export function ShortcutsTable() {
+ const reduce = useReducedMotion();
+
+ return (
+
+
+
+
+
+
+
+
+ Shortcut
+
+
+ Action
+
+
+
+
+ {shortcuts.map((row) => (
+
+
+
+ {row.keys}
+
+
+
+ {row.action}
+
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/Website/components/site-footer.tsx b/Website/components/site-footer.tsx
new file mode 100644
index 0000000..b1fd671
--- /dev/null
+++ b/Website/components/site-footer.tsx
@@ -0,0 +1,47 @@
+import Link from "next/link";
+import { DOWNLOAD_URL, GITHUB_URL } from "@/lib/site";
+
+export function SiteFooter() {
+ return (
+
+ );
+}
diff --git a/Website/components/site-nav.tsx b/Website/components/site-nav.tsx
new file mode 100644
index 0000000..779969d
--- /dev/null
+++ b/Website/components/site-nav.tsx
@@ -0,0 +1,131 @@
+"use client";
+
+import * as React from "react";
+import Link from "next/link";
+import { Menu, Moon, Sun, X } from "lucide-react";
+import { useTheme } from "next-themes";
+import { cn } from "@/lib/utils";
+import { DOWNLOAD_URL, GITHUB_URL } from "@/lib/site";
+import { ShimmerButton } from "@/components/parable/shimmer-button";
+
+function GithubMark() {
+ return (
+
+
+
+ );
+}
+
+const LINKS = [
+ { href: "#features", label: "Features" },
+ { href: "#workflow", label: "Workflow" },
+ { href: "#privacy", label: "Privacy" },
+ { href: "#guides", label: "Guides" },
+ { href: "#faq", label: "FAQ" },
+];
+
+export function SiteNav() {
+ const [mobileOpen, setMobileOpen] = React.useState(false);
+
+ return (
+
+ );
+}
+
+function ThemeToggle() {
+ const { resolvedTheme, setTheme } = useTheme();
+ const [mounted, setMounted] = React.useState(false);
+ React.useEffect(() => setMounted(true), []);
+ const dark = resolvedTheme === "dark";
+
+ return (
+ setTheme(dark ? "light" : "dark")}
+ aria-label="Toggle theme"
+ className="inline-flex size-9 items-center justify-center rounded-lg border text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
+ >
+ {mounted && dark ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/Website/components/theme-provider.tsx b/Website/components/theme-provider.tsx
new file mode 100644
index 0000000..0874685
--- /dev/null
+++ b/Website/components/theme-provider.tsx
@@ -0,0 +1,11 @@
+"use client";
+
+import * as React from "react";
+import { ThemeProvider as NextThemes } from "next-themes";
+
+export function ThemeProvider({
+ children,
+ ...props
+}: React.ComponentProps) {
+ return {children} ;
+}
diff --git a/Website/components/workflow-steps.tsx b/Website/components/workflow-steps.tsx
new file mode 100644
index 0000000..67f2e1d
--- /dev/null
+++ b/Website/components/workflow-steps.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import { Camera, ClipboardCopy, PenLine } from "lucide-react";
+import { motion, useReducedMotion } from "motion/react";
+import { SectionHeader } from "@/components/section-header";
+import { workflow } from "@/lib/site";
+
+const STEP_ICONS = [Camera, PenLine, ClipboardCopy];
+
+export function WorkflowSteps() {
+ const reduce = useReducedMotion();
+
+ return (
+
+
+
+
+ {workflow.map((step, i) => {
+ const Icon = STEP_ICONS[i] ?? Camera;
+ return (
+
+
+
+
+ {step.step}
+
+
+ {step.title}
+
+ {step.body}
+
+
+ );
+ })}
+
+
+
+ );
+}
diff --git a/Website/downloads/Notable.zip b/Website/downloads/Notable.zip
deleted file mode 100644
index 61ed684..0000000
Binary files a/Website/downloads/Notable.zip and /dev/null differ
diff --git a/Website/index.html b/Website/index.html
deleted file mode 100644
index 8712a1d..0000000
--- a/Website/index.html
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
- Notable - Local Capture for macOS
-
-
-
-
-
-
-
-
-
-
Native macOS Capture app
-
Notable
-
- Capture, annotate, record, redact, and export from one quiet menu bar app. Built for
- fast local work, with no network AI calls.
-
-
-
Current local test build: macOS 13+, Apple Silicon and Intel.
-
-
-
-
-
-
Built for daily Capture work
-
A focused tool for grabbing pixels, marking them up, and moving on.
-
-
- Notable starts from the menu bar, freezes the display, lets you choose a Selection, and
- opens an Editor where every Annotation stays editable until you copy, save, or return to it
- from local history.
-
-
-
-
-
- 01
- Capture and annotate
-
- Region and window Selection, arrows, rectangles, text, pencil, Censor, stamps,
- highlighter, measure, spotlight, loupe, and eyedropper.
-
-
-
- 02
- Record and export
-
- MP4 display recording, trim controls, GIF export, all-display Capture, scroll Capture,
- and PNG, JPEG, HEIC, or TIFF image output.
-
-
-
- 03
- Local intelligence
-
- On-device Vision OCR, QR detection, face detection, and local sensitive-text or face
- Censoring without sending Capture data away.
-
-
-
-
-
-
-
Private by default
-
No cloud dependency. No network AI. No pretend upload button.
-
- History, brand kits, OCR, face detection, redaction, adjustments, and export are local.
- Upload and sync are intentionally absent until a real backend, domain, and auth policy
- are configured.
-
-
-
-
- Screen Recording permission is required before Capture or recording.
-
-
-
-
-
-
Download
-
Try the current Mac build.
-
- This is a development build for testing. Final branding, signing, notarization, and
- public release packaging still need to happen before broad distribution.
-
-
-
- Download and unzip Notable.zip .
- Open Notable, then grant Screen Recording when macOS asks.
- Quit and reopen Notable after granting permission.
- Use Command-Shift-2 to start a Capture, or use the menu bar for recording.
-
-
-
-
-
-
-
-
-
diff --git a/Website/lib/site.ts b/Website/lib/site.ts
new file mode 100644
index 0000000..e0315f2
--- /dev/null
+++ b/Website/lib/site.ts
@@ -0,0 +1,145 @@
+export const stats = [
+ { value: "14", label: "Annotation tools" },
+ { value: "30", label: "Beautify gradients" },
+ { value: "4", label: "Export formats" },
+ { value: "0", label: "Cloud AI calls" },
+];
+
+export const logos = [
+ "ScreenCaptureKit",
+ "Vision",
+ "Core Image",
+ "AVFoundation",
+ "SwiftUI",
+ "Supabase",
+];
+
+export const features = [
+ {
+ id: "capture",
+ title: "Instant Capture",
+ body: "Global hotkey freezes every display. Drag a region, snap to windows, or stitch all screens β pixels stay full resolution.",
+ size: "wide" as const,
+ icon: "camera",
+ },
+ {
+ id: "tools",
+ title: "14 annotation tools",
+ body: "Arrows Γ5 styles, ellipse, censor Γ4 modes, stamps, spotlight, measure, and utility loupe + eyedropper.",
+ size: "sm" as const,
+ icon: "pen",
+ },
+ {
+ id: "scroll",
+ title: "Scroll Capture",
+ body: "Select a tall region from the Overlay, scroll the source, and stitch with on-device Vision registration.",
+ size: "sm" as const,
+ icon: "scroll",
+ },
+ {
+ id: "record",
+ title: "Screen recording",
+ body: "MP4 at 30/60/120 fps with system audio, trim editor, and local GIF export β no subscription recorder.",
+ size: "wide" as const,
+ icon: "video",
+ },
+ {
+ id: "beautify",
+ title: "Beautify",
+ body: "30 gradient backgrounds, window chrome, padding, radius, shadow, and saved brand kits for consistent ship-ready Captures.",
+ size: "sm" as const,
+ icon: "sparkles",
+ },
+ {
+ id: "vision",
+ title: "Local Vision",
+ body: "OCR, QR, face detection, regex PII censoring, and on-device translation β nothing leaves your Mac unless you upload.",
+ size: "tall" as const,
+ icon: "eye",
+ },
+ {
+ id: "history",
+ title: "Capture history",
+ body: "Disk-backed documents restore annotations, adjustments, beautify, and output format β re-edit any past Capture.",
+ size: "sm" as const,
+ icon: "history",
+ },
+ {
+ id: "upload",
+ title: "Supabase upload",
+ body: "Configure your bucket once, upload from the Editor, and copy a public link. Real Storage β not a pretend button.",
+ size: "sm" as const,
+ icon: "cloud",
+ },
+];
+
+export const workflow = [
+ {
+ step: "01",
+ title: "Freeze & select",
+ body: "Press ββ§2 (configurable). ScreenCaptureKit captures every display; the Overlay lets you drag, snap, or scroll-capture.",
+ },
+ {
+ step: "02",
+ title: "Mark up locally",
+ body: "Annotate, censor, beautify, and adjust in the Editor. Undo covers the Layer stack; settings stay document-level.",
+ },
+ {
+ step: "03",
+ title: "Copy, save, or upload",
+ body: "PNG/JPEG/HEIC/TIFF export matches what you see on screen. Optional Supabase upload copies a link to the clipboard.",
+ },
+];
+
+export const shortcuts = [
+ { keys: "ββ§2", action: "Capture region (configurable in Preferences)" },
+ { keys: "βC", action: "Copy Capture from Editor" },
+ { keys: "βS", action: "Save Capture" },
+ { keys: "βZ / β§βZ", action: "Undo / redo annotations" },
+ { keys: "Tab", action: "Window snap in Overlay" },
+ { keys: "Esc", action: "Cancel Overlay or exit utility tool" },
+];
+
+export const guides = [
+ {
+ title: "Redact sensitive text locally",
+ body: "Inspect Capture β Recognize Text β Censor Detected Sensitive Text. Regex runs on-device via Vision.",
+ },
+ {
+ title: "Scroll a long page",
+ body: "In the Overlay choose Scroll Capture, drag a tall region, then add frames from the menu bar and finish.",
+ },
+ {
+ title: "Save a brand kit",
+ body: "Enable Beautify, tune padding and gradient, then save a named kit from the Beautify panel.",
+ },
+ {
+ title: "Upload to Supabase",
+ body: "Paste project URL, anon key, and bucket in Preferences. Upload from the Editor copies the link.",
+ },
+];
+
+export const faqs = [
+ {
+ q: "Does Parcel send my Captures to the cloud for AI?",
+ a: "No. OCR, face detection, translation, and redaction use Apple on-device frameworks only. Upload is optional and only when you configure Supabase.",
+ },
+ {
+ q: "Why does macOS ask for Screen Recording permission?",
+ a: "ScreenCaptureKit requires Screen Recording TCC before any Capture or recording. Quit and reopen Parcel after granting.",
+ },
+ {
+ q: "Is the download signed and notarized?",
+ a: "Release builds are Developer ID signed and notarized. Run Scripts/release.sh with your Apple Developer credentials, or download from parcel.parable.dev.",
+ },
+ {
+ q: "How do I configure Supabase upload?",
+ a: "Create a public Storage bucket, paste the project URL and anon key in Preferences, and set an optional custom public base URL.",
+ },
+];
+
+export const DOWNLOAD_URL = "/downloads/Parcel.zip";
+export const HOMEBREW_CMD = "brew install --cask parcel";
+export const GITHUB_URL = "https://github.com/bswxyz/notable";
+export const SITE_URL =
+ process.env.NEXT_PUBLIC_SITE_URL ?? "https://parcel.parable.dev";
diff --git a/Website/lib/utils.ts b/Website/lib/utils.ts
new file mode 100644
index 0000000..a5ef193
--- /dev/null
+++ b/Website/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/Website/next.config.ts b/Website/next.config.ts
new file mode 100644
index 0000000..a7d4cbc
--- /dev/null
+++ b/Website/next.config.ts
@@ -0,0 +1,8 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ output: "export",
+ images: { unoptimized: true },
+};
+
+export default nextConfig;
diff --git a/Website/package-lock.json b/Website/package-lock.json
new file mode 100644
index 0000000..cc97937
--- /dev/null
+++ b/Website/package-lock.json
@@ -0,0 +1,7076 @@
+{
+ "name": "parcel-website",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "parcel-website",
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "@base-ui/react": "^1.6.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^1.25.0",
+ "motion": "^12.42.2",
+ "next": "16.2.10",
+ "next-themes": "^0.4.6",
+ "react": "19.2.4",
+ "react-dom": "19.2.4",
+ "tailwind-merge": "^3.6.0",
+ "tw-animate-css": "^1.4.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "16.2.10",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/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/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@base-ui/react": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.6.0.tgz",
+ "integrity": "sha512-/jzjTWJYXhRFO45Bev9lc3cHbmjzCMpUqbMZ2AgKy/z25mY9B6shGSNcXcjQar9n5doM0KYW1W8fcFv2jZBuMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@base-ui/utils": "0.3.1",
+ "@floating-ui/react-dom": "^2.1.8",
+ "@floating-ui/utils": "^0.2.11",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui-org"
+ },
+ "peerDependencies": {
+ "@date-fns/tz": "^1.2.0",
+ "@types/react": "^17 || ^18 || ^19",
+ "date-fns": "^4.0.0",
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "@date-fns/tz": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@base-ui/utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.3.1.tgz",
+ "integrity": "sha512-gFFiltORVmW/N6IILTGxizP3PBpVpysqML1ALY5Vk0mH+7faVkCknOU31goYHN5Aoek2dkjxva1XOD2Ce9WuIg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.29.2",
+ "@floating-ui/utils": "^0.2.11",
+ "reselect": "^5.2.0",
+ "use-sync-external-store": "^1.6.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^17 || ^18 || ^19",
+ "react": "^17 || ^18 || ^19",
+ "react-dom": "^17 || ^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
+ "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.1",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz",
+ "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
+ "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.5"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
+ "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.14.0",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.3.0",
+ "minimatch": "^3.1.5",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
+ "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.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/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
+ "cpu": [
+ "arm"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
+ "cpu": [
+ "s390x"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz",
+ "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==",
+ "license": "MIT"
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.10.tgz",
+ "integrity": "sha512-Gs8D2m21VnJeFo9qvYIIqJH94frWerWYu41BprU1pLtRVF7PCQNLiFZZ3fG+iPuj3K83Cwv/rt+msLOy8Qgu3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "3.3.1"
+ }
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz",
+ "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz",
+ "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz",
+ "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz",
+ "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz",
+ "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz",
+ "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==",
+ "cpu": [
+ "x64"
+ ],
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz",
+ "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz",
+ "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "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/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.4.0"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz",
+ "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "enhanced-resolve": "^5.24.1",
+ "jiti": "^2.7.0",
+ "lightningcss": "1.32.0",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.3.3"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz",
+ "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.3.3",
+ "@tailwindcss/oxide-darwin-arm64": "4.3.3",
+ "@tailwindcss/oxide-darwin-x64": "4.3.3",
+ "@tailwindcss/oxide-freebsd-x64": "4.3.3",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.3.3",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.3.3",
+ "@tailwindcss/oxide-linux-x64-musl": "4.3.3",
+ "@tailwindcss/oxide-wasm32-wasi": "4.3.3",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.3.3"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz",
+ "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz",
+ "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz",
+ "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz",
+ "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz",
+ "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz",
+ "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz",
+ "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz",
+ "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz",
+ "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz",
+ "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.11.1",
+ "@emnapi/runtime": "^1.11.1",
+ "@emnapi/wasi-threads": "^1.2.2",
+ "@napi-rs/wasm-runtime": "^1.1.4",
+ "@tybys/wasm-util": "^0.10.2",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz",
+ "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz",
+ "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.3.tgz",
+ "integrity": "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.3.3",
+ "@tailwindcss/oxide": "4.3.3",
+ "postcss": "^8.5.16",
+ "tailwindcss": "4.3.3"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.43",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
+ "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.2.17",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
+ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.2.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.2.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz",
+ "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/type-utils": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.64.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
+ "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz",
+ "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz",
+ "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.64.0",
+ "@typescript-eslint/types": "^8.64.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz",
+ "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz",
+ "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz",
+ "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz",
+ "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz",
+ "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.64.0",
+ "@typescript-eslint/tsconfig-utils": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/visitor-keys": "8.64.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
+ "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz",
+ "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.64.0",
+ "@typescript-eslint/types": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz",
+ "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.64.0",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-android-arm-eabi": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz",
+ "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-android-arm64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz",
+ "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-arm64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz",
+ "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-darwin-x64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz",
+ "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-freebsd-x64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz",
+ "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz",
+ "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz",
+ "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz",
+ "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-arm64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz",
+ "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-loong64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz",
+ "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-loong64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz",
+ "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz",
+ "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz",
+ "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-riscv64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz",
+ "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-s390x-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz",
+ "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-gnu": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz",
+ "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-linux-x64-musl": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz",
+ "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-openharmony-arm64": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz",
+ "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz",
+ "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "1.10.0",
+ "@emnapi/runtime": "1.10.0",
+ "@napi-rs/wasm-runtime": "^1.1.4"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
+ "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
+ "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-ia32-msvc": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz",
+ "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz",
+ "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.15.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
+ "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlast": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz",
+ "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.tosorted": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz",
+ "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3",
+ "es-errors": "^1.3.0",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz",
+ "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.43",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz",
+ "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.6",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz",
+ "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.42",
+ "caniuse-lite": "^1.0.30001803",
+ "electron-to-chromium": "^1.5.389",
+ "node-releases": "^2.0.51",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
+ "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "get-intrinsic": "^1.3.0",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001806",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz",
+ "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/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",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.393",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz",
+ "integrity": "sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.24.2",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.2.tgz",
+ "integrity": "sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.2",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz",
+ "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-abstract-get": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz",
+ "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.2",
+ "is-callable": "^1.2.7",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-iterator-helpers": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz",
+ "integrity": "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.2",
+ "es-errors": "^1.3.0",
+ "es-set-tostringtag": "^2.1.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.3.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "iterator.prototype": "^1.1.5",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz",
+ "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-abstract-get": "^1.0.0",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.1.0",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz",
+ "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.2",
+ "@eslint/config-helpers": "^0.4.2",
+ "@eslint/core": "^0.17.0",
+ "@eslint/eslintrc": "^3.3.6",
+ "@eslint/js": "9.39.5",
+ "@eslint/plugin-kit": "^0.4.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "ajv": "^6.14.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.5",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.2.10.tgz",
+ "integrity": "sha512-HSybLOY0QKf39i4FWUqPN0xWiNDi6A6UqJmZtgDkS3zMqjXTqULvj/sueXx3cdCG0mVG+qH6k5/qdegklH1d1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@next/eslint-plugin-next": "16.2.10",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-import-resolver-typescript": "^3.5.2",
+ "eslint-plugin-import": "^2.32.0",
+ "eslint-plugin-jsx-a11y": "^6.10.0",
+ "eslint-plugin-react": "^7.37.0",
+ "eslint-plugin-react-hooks": "^7.0.0",
+ "globals": "16.4.0",
+ "typescript-eslint": "^8.46.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=9.0.0",
+ "typescript": ">=3.3.1"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-next/node_modules/globals": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.10",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz",
+ "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.16.1",
+ "resolve": "^2.0.0-next.6"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz",
+ "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@nolyfill/is-core-module": "1.0.39",
+ "debug": "^4.4.0",
+ "get-tsconfig": "^4.10.0",
+ "is-bun-module": "^2.0.0",
+ "stable-hash": "^0.0.5",
+ "tinyglobby": "^0.2.13",
+ "unrs-resolver": "^1.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-import-resolver-typescript"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*",
+ "eslint-plugin-import-x": "*"
+ },
+ "peerDependenciesMeta": {
+ "eslint-plugin-import": {
+ "optional": true
+ },
+ "eslint-plugin-import-x": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz",
+ "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aria-query": "^5.3.2",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-react": {
+ "version": "7.37.5",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz",
+ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.8",
+ "array.prototype.findlast": "^1.2.5",
+ "array.prototype.flatmap": "^1.3.3",
+ "array.prototype.tosorted": "^1.1.4",
+ "doctrine": "^2.1.0",
+ "es-iterator-helpers": "^1.2.1",
+ "estraverse": "^5.3.0",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+ "minimatch": "^3.1.2",
+ "object.entries": "^1.1.9",
+ "object.fromentries": "^2.0.8",
+ "object.values": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "resolve": "^2.0.0-next.5",
+ "semver": "^6.3.1",
+ "string.prototype.matchall": "^4.0.12",
+ "string.prototype.repeat": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7"
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz",
+ "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
+ "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
+ "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/framer-motion": {
+ "version": "12.42.2",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz",
+ "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.42.2",
+ "motion-utils": "^12.39.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
+ "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2",
+ "hasown": "^2.0.4",
+ "is-callable": "^1.2.7",
+ "is-document.all": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
+ "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bun-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
+ "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.7.1"
+ }
+ },
+ "node_modules/is-bun-module/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
+ "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-document.all": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",
+ "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "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"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isexe": {
+ "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/iterator.prototype": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz",
+ "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "get-proto": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz",
+ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.32.0",
+ "lightningcss-darwin-arm64": "1.32.0",
+ "lightningcss-darwin-x64": "1.32.0",
+ "lightningcss-freebsd-x64": "1.32.0",
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
+ "lightningcss-linux-arm64-gnu": "1.32.0",
+ "lightningcss-linux-arm64-musl": "1.32.0",
+ "lightningcss-linux-x64-gnu": "1.32.0",
+ "lightningcss-linux-x64-musl": "1.32.0",
+ "lightningcss-win32-arm64-msvc": "1.32.0",
+ "lightningcss-win32-x64-msvc": "1.32.0"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.32.0",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "1.25.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.25.0.tgz",
+ "integrity": "sha512-/mdJTRbiwcLOQ1NZZK1amZF9rIZyvO18D6r9TngE6TG1NmqHgFuT4eE7Xrkm9UsXMbBJD1NlfwHVltCDWHrOTw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/motion": {
+ "version": "12.42.2",
+ "resolved": "https://registry.npmjs.org/motion/-/motion-12.42.2.tgz",
+ "integrity": "sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q==",
+ "license": "MIT",
+ "dependencies": {
+ "framer-motion": "^12.42.2",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.42.2",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz",
+ "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.39.0"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.39.0",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
+ "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-postinstall": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+ "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "napi-postinstall": "lib/cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/napi-postinstall"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/next": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz",
+ "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "16.2.10",
+ "@swc/helpers": "0.5.15",
+ "baseline-browser-mapping": "^2.9.19",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "16.2.10",
+ "@next/swc-darwin-x64": "16.2.10",
+ "@next/swc-linux-arm64-gnu": "16.2.10",
+ "@next/swc-linux-arm64-musl": "16.2.10",
+ "@next/swc-linux-x64-gnu": "16.2.10",
+ "@next/swc-linux-x64-musl": "16.2.10",
+ "@next/swc-win32-arm64-msvc": "16.2.10",
+ "@next/swc-win32-x64-msvc": "16.2.10",
+ "sharp": "^0.34.5"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.51.1",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next-themes": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
+ "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/node-exports-info": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz",
+ "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array.prototype.flatmap": "^1.3.3",
+ "es-errors": "^1.3.0",
+ "object.entries": "^1.1.9",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.51",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz",
+ "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "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"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
+ "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.2.4",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
+ "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.4",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
+ "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.4"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reselect": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz",
+ "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==",
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "2.0.0-next.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz",
+ "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.2",
+ "node-exports-info": "^1.6.0",
+ "object-keys": "^1.1.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
+ "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "get-intrinsic": "^1.3.0",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/sharp/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stable-hash": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
+ "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+ "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.repeat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz",
+ "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz",
+ "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.2",
+ "es-object-atoms": "^1.1.2",
+ "has-property-descriptors": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz",
+ "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz",
+ "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz",
+ "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz",
+ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tw-animate-css": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
+ "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz",
+ "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "for-each": "^0.3.5",
+ "gopd": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "possible-typed-array-names": "^1.1.0",
+ "reflect.getprototypeof": "^1.0.10"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.64.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz",
+ "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.64.0",
+ "@typescript-eslint/parser": "8.64.0",
+ "@typescript-eslint/typescript-estree": "8.64.0",
+ "@typescript-eslint/utils": "8.64.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unrs-resolver": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz",
+ "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "napi-postinstall": "^0.3.4"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unrs-resolver"
+ },
+ "optionalDependencies": {
+ "@unrs/resolver-binding-android-arm-eabi": "1.12.2",
+ "@unrs/resolver-binding-android-arm64": "1.12.2",
+ "@unrs/resolver-binding-darwin-arm64": "1.12.2",
+ "@unrs/resolver-binding-darwin-x64": "1.12.2",
+ "@unrs/resolver-binding-freebsd-x64": "1.12.2",
+ "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2",
+ "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2",
+ "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-arm64-musl": "1.12.2",
+ "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-loong64-musl": "1.12.2",
+ "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2",
+ "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-x64-gnu": "1.12.2",
+ "@unrs/resolver-binding-linux-x64-musl": "1.12.2",
+ "@unrs/resolver-binding-openharmony-arm64": "1.12.2",
+ "@unrs/resolver-binding-wasm32-wasi": "1.12.2",
+ "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2",
+ "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2",
+ "@unrs/resolver-binding-win32-x64-msvc": "1.12.2"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/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"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.22",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz",
+ "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
+ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ }
+ }
+}
diff --git a/Website/package.json b/Website/package.json
new file mode 100644
index 0000000..956860e
--- /dev/null
+++ b/Website/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "parcel-website",
+ "version": "1.0.0",
+ "private": true,
+ "license": "MIT",
+ "scripts": {
+ "dev": "next dev --port 3000",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint"
+ },
+ "dependencies": {
+ "@base-ui/react": "^1.6.0",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^1.25.0",
+ "motion": "^12.42.2",
+ "next": "16.2.10",
+ "next-themes": "^0.4.6",
+ "react": "19.2.4",
+ "react-dom": "19.2.4",
+ "tailwind-merge": "^3.6.0",
+ "tw-animate-css": "^1.4.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "16.2.10",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/Website/postcss.config.mjs b/Website/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/Website/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/Website/public/appcast.xml b/Website/public/appcast.xml
new file mode 100644
index 0000000..bf00dd3
--- /dev/null
+++ b/Website/public/appcast.xml
@@ -0,0 +1,28 @@
+
+
+
+ Parcel
+ https://parcel.parable.dev
+ Parcel for macOS release feed
+ en
+ -
+
1.0.0
+ 1
+ 1.0.0
+ Fri, 17 Jul 2026 18:00:00 +0000
+
+ Parcel 1.0.0
+
+ Initial public release β Capture, annotate, record, Vision, history, and optional Supabase upload.
+ Rebranded from the Notable codename to Parcel (Parable ecosystem).
+
+ ]]>
+
+
+
diff --git a/Website/public/assets/notable-permission.svg b/Website/public/assets/notable-permission.svg
new file mode 100644
index 0000000..973567d
--- /dev/null
+++ b/Website/public/assets/notable-permission.svg
@@ -0,0 +1,9 @@
+
+
+ Screen & System Audio Recording
+
+
+ Parcel
+ On
+ Allow Parcel to record the contents of your display and audio.
+
diff --git a/Website/public/downloads/Parcel.zip b/Website/public/downloads/Parcel.zip
new file mode 100644
index 0000000..d8af1e1
Binary files /dev/null and b/Website/public/downloads/Parcel.zip differ
diff --git a/Website/public/downloads/README.md b/Website/public/downloads/README.md
new file mode 100644
index 0000000..ec6be61
--- /dev/null
+++ b/Website/public/downloads/README.md
@@ -0,0 +1,12 @@
+Place `Parcel.zip` here before deploying the marketing site.
+
+Generate a Debug zip for local testing:
+
+```bash
+xcodegen generate
+xcodebuild -project Parcel.xcodeproj -scheme Parcel -configuration Debug \
+ -derivedDataPath .derivedData build CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=YES
+cd .derivedData/Build/Products/Debug && zip -r ../../../../Website/public/downloads/Parcel.zip Parcel.app
+```
+
+For production, use `Scripts/release.sh` with Developer ID signing and notarization.
diff --git a/Website/public/favicon.svg b/Website/public/favicon.svg
new file mode 100644
index 0000000..19f7359
--- /dev/null
+++ b/Website/public/favicon.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/Website/public/og.svg b/Website/public/og.svg
new file mode 100644
index 0000000..16add50
--- /dev/null
+++ b/Website/public/og.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Parcel
+ Local Capture for macOS β annotate, record, redact
+
+ Download free
+
diff --git a/Website/script.js b/Website/script.js
deleted file mode 100644
index 030eef8..0000000
--- a/Website/script.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const downloadLinks = document.querySelectorAll('a[href="downloads/Notable.zip"]');
-
-downloadLinks.forEach((link) => {
- link.addEventListener("click", () => {
- link.dataset.clicked = "true";
- });
-});
diff --git a/Website/styles.css b/Website/styles.css
deleted file mode 100644
index 78ddbdb..0000000
--- a/Website/styles.css
+++ /dev/null
@@ -1,327 +0,0 @@
-:root {
- color-scheme: dark;
- --bg: #101112;
- --panel: #1b1d1e;
- --panel-strong: #232527;
- --text: #f3f0ea;
- --muted: #b8b2aa;
- --line: rgba(255, 255, 255, 0.13);
- --coral: #ff5a57;
- --mint: #86e3b4;
- --gold: #f0c46a;
- --ink: #070707;
- font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
-}
-
-* {
- box-sizing: border-box;
-}
-
-html {
- scroll-behavior: smooth;
-}
-
-body {
- margin: 0;
- background: var(--bg);
- color: var(--text);
-}
-
-a {
- color: inherit;
-}
-
-.site-header {
- position: fixed;
- z-index: 10;
- top: 0;
- left: 0;
- right: 0;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 24px;
- padding: 18px clamp(18px, 5vw, 64px);
- background: rgba(9, 9, 9, 0.68);
- border-bottom: 1px solid var(--line);
- backdrop-filter: blur(18px);
-}
-
-.brand,
-nav {
- display: flex;
- align-items: center;
- gap: 18px;
-}
-
-.brand {
- text-decoration: none;
- font-weight: 800;
-}
-
-.brand-mark {
- width: 16px;
- height: 16px;
- border-radius: 50%;
- background: linear-gradient(135deg, var(--coral), var(--gold));
- box-shadow: 0 0 0 5px rgba(255, 90, 87, 0.13);
-}
-
-nav a,
-footer a {
- color: var(--muted);
- font-size: 0.94rem;
- text-decoration: none;
-}
-
-nav a:hover,
-footer a:hover {
- color: var(--text);
-}
-
-.hero {
- position: relative;
- min-height: 88vh;
- display: flex;
- align-items: center;
- padding: 120px clamp(22px, 7vw, 92px) 78px;
- overflow: hidden;
- border-bottom: 1px solid var(--line);
-}
-
-.hero::before {
- content: "";
- position: absolute;
- inset: 0;
- background:
- linear-gradient(90deg, rgba(7, 7, 7, 0.94) 0%, rgba(7, 7, 7, 0.74) 44%, rgba(7, 7, 7, 0.24) 100%),
- linear-gradient(0deg, rgba(16, 17, 18, 0.92) 0%, rgba(16, 17, 18, 0.06) 44%),
- url("assets/notable-permission.png") right center / min(820px, 74vw) auto no-repeat;
- transform: scale(1.03);
-}
-
-.hero-content {
- position: relative;
- max-width: 720px;
-}
-
-.eyebrow,
-.section-kicker,
-.release-note,
-figcaption {
- color: var(--muted);
-}
-
-.eyebrow,
-.section-kicker {
- margin: 0 0 14px;
- font-size: 0.78rem;
- font-weight: 800;
- letter-spacing: 0;
- text-transform: uppercase;
-}
-
-h1,
-h2,
-h3,
-p {
- margin-top: 0;
-}
-
-h1 {
- margin-bottom: 20px;
- font-size: clamp(4rem, 12vw, 10rem);
- line-height: 0.88;
- letter-spacing: 0;
-}
-
-h2 {
- max-width: 760px;
- font-size: clamp(2rem, 5vw, 4.5rem);
- line-height: 1;
- letter-spacing: 0;
-}
-
-h3 {
- margin-bottom: 12px;
- font-size: 1.2rem;
- letter-spacing: 0;
-}
-
-p {
- color: var(--muted);
- line-height: 1.65;
-}
-
-.hero-copy {
- max-width: 620px;
- color: #ded8cf;
- font-size: clamp(1.1rem, 2vw, 1.36rem);
-}
-
-.hero-actions {
- display: flex;
- flex-wrap: wrap;
- gap: 12px;
- margin: 32px 0 16px;
-}
-
-.button {
- display: inline-flex;
- min-height: 46px;
- align-items: center;
- justify-content: center;
- padding: 0 20px;
- border: 1px solid var(--line);
- border-radius: 8px;
- font-weight: 800;
- text-decoration: none;
-}
-
-.button.primary {
- background: var(--coral);
- border-color: var(--coral);
- color: var(--ink);
-}
-
-.button.secondary {
- background: rgba(255, 255, 255, 0.08);
- color: var(--text);
-}
-
-.intro,
-.split-section,
-.install-section {
- display: grid;
- grid-template-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr);
- gap: clamp(28px, 6vw, 76px);
- padding: clamp(64px, 9vw, 116px) clamp(22px, 7vw, 92px);
- border-bottom: 1px solid var(--line);
-}
-
-.intro > p,
-.split-section > div p,
-.install-section > div p {
- max-width: 650px;
- font-size: 1.06rem;
-}
-
-.feature-grid {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 1px;
- background: var(--line);
- border-bottom: 1px solid var(--line);
-}
-
-.feature-grid article {
- min-height: 330px;
- padding: clamp(26px, 4vw, 46px);
- background: var(--panel);
-}
-
-.feature-index {
- display: block;
- margin-bottom: 74px;
- color: var(--gold);
- font-size: 0.84rem;
- font-weight: 900;
-}
-
-figure {
- margin: 0;
-}
-
-figure img {
- display: block;
- width: 100%;
- border: 1px solid var(--line);
- border-radius: 8px;
- background: var(--panel-strong);
-}
-
-figcaption {
- margin-top: 12px;
- font-size: 0.9rem;
-}
-
-.steps {
- margin: 0;
- padding: 0;
- list-style: none;
- counter-reset: step;
-}
-
-.steps li {
- counter-increment: step;
- position: relative;
- padding: 18px 0 18px 46px;
- border-top: 1px solid var(--line);
- color: #d8d2ca;
-}
-
-.steps li::before {
- content: counter(step);
- position: absolute;
- left: 0;
- top: 17px;
- width: 28px;
- color: var(--mint);
- font-weight: 900;
-}
-
-.steps a {
- color: var(--mint);
- font-weight: 800;
-}
-
-footer {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 18px;
- padding: 26px clamp(22px, 7vw, 92px);
- color: var(--muted);
- background: #0a0b0b;
-}
-
-@media (max-width: 860px) {
- .site-header {
- position: absolute;
- }
-
- nav {
- display: none;
- }
-
- .hero {
- min-height: 86vh;
- align-items: flex-end;
- }
-
- .hero::before {
- background:
- linear-gradient(0deg, rgba(7, 7, 7, 0.98) 0%, rgba(7, 7, 7, 0.72) 54%, rgba(7, 7, 7, 0.32) 100%),
- url("assets/notable-permission.png") center 82px / 104vw auto no-repeat;
- }
-
- .intro,
- .split-section,
- .install-section,
- .feature-grid {
- grid-template-columns: 1fr;
- }
-
- .feature-grid article {
- min-height: auto;
- }
-
- .feature-index {
- margin-bottom: 36px;
- }
-
- footer {
- align-items: flex-start;
- flex-direction: column;
- }
-}
diff --git a/Website/tsconfig.json b/Website/tsconfig.json
new file mode 100644
index 0000000..e7ff3a2
--- /dev/null
+++ b/Website/tsconfig.json
@@ -0,0 +1,41 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/Website/vercel.json b/Website/vercel.json
new file mode 100644
index 0000000..7c2e66a
--- /dev/null
+++ b/Website/vercel.json
@@ -0,0 +1,13 @@
+{
+ "headers": [
+ {
+ "source": "/downloads/(.*)",
+ "headers": [
+ {
+ "key": "Content-Disposition",
+ "value": "attachment"
+ }
+ ]
+ }
+ ]
+}
diff --git a/docs/MACOS13_VM_QA.md b/docs/MACOS13_VM_QA.md
new file mode 100644
index 0000000..0e8e9a9
--- /dev/null
+++ b/docs/MACOS13_VM_QA.md
@@ -0,0 +1,41 @@
+# macOS 13β14 Fallback QA
+
+Parcelβs primary development machine runs macOS 26. Capture and recording include fallback code paths for older macOS versions that **must be verified on real hardware or a VM** before claiming macOS 13.0+ support.
+
+## Setup a macOS 13 VM
+
+1. Install [UTM](https://mac.getutm.app/) or use Parallels with a macOS 13 Ventura IPSW.
+2. Copy `Parcel.zip` (Release build) into the VM.
+3. Grant **Screen Recording** in System Settings β Privacy & Security.
+4. Quit and reopen Parcel.
+
+## Paths under test
+
+| Feature | macOS version | Implementation |
+|---------|---------------|----------------|
+| Capture | 13 | `SingleFrameStreamCapturer` in `Sources/Parcel/Capture/CaptureEngine.swift` |
+| Recording | 13β14 | `LegacyRecordingWriter` in `Sources/Parcel/Recording/ScreenRecorder.swift` |
+| Recording (native) | 15+ | `SCRecordingOutput` |
+
+On macOS 14, Capture uses `SCScreenshotManager` but recording still uses the legacy writer.
+
+## Minimum matrix (VM)
+
+Run sections **1, 5, 8, 9** from [QA_CHECKLIST.md](QA_CHECKLIST.md).
+
+Additional checks:
+
+- [ ] Confirm Capture does **not** crash (SCStream fallback)
+- [ ] Confirm recording produces playable MP4 with audio (AVFoundation writer)
+- [ ] Translation button hidden or disabled (requires macOS 15+)
+
+## Logging
+
+If Capture fails on macOS 13, check Console.app for `Parcel:` messages from `CaptureEngine`.
+
+## Sign-off
+
+| Tester | macOS version | Date | Pass |
+|--------|---------------|------|------|
+| | 13.x | | β |
+| | 14.x | | β |
diff --git a/docs/QA_CHECKLIST.md b/docs/QA_CHECKLIST.md
new file mode 100644
index 0000000..a4f153e
--- /dev/null
+++ b/docs/QA_CHECKLIST.md
@@ -0,0 +1,198 @@
+# Parcel β Manual QA Checklist
+
+Run on macOS 26 with **Screen Recording** granted. Use a **Release + sandboxed** build for final sign-off.
+
+**Build under test:** _______________
+**Tester:** _______________
+**Date:** _______________
+
+## Prerequisites
+
+- [ ] Screen Recording granted for Parcel in System Settings β Privacy & Security
+- [ ] App quit and reopened after granting permission
+- [ ] At least one external display connected (for multi-monitor tests, optional)
+
+---
+
+## 1. Region Capture β Editor β Copy/Save
+
+- [ ] Press ββ§2 (or configured hotkey) β frozen Overlay appears on all displays
+- [ ] Drag a region β dimensions label updates
+- [ ] Release β Editor opens with cropped Capture
+- [ ] βC copies to clipboard β paste into Preview/Messages matches on-screen Canvas
+- [ ] βS saves PNG β file matches on-screen Canvas
+- [ ] Save as JPEG, HEIC, TIFF β each format opens correctly
+
+**Notes:** _______________
+
+---
+
+## 2. Window Snap + Aspect Presets + Delay
+
+- [ ] Hover over window β highlight appears
+- [ ] Single-click window β snaps Selection to window bounds
+- [ ] Tab cycles aspect presets during drag
+- [ ] Edge snap works near screen edges
+- [ ] Menu: Capture After 3s β countdown in menu bar, then Overlay
+- [ ] Capture After 5s / 10s work
+
+**Notes:** _______________
+
+---
+
+## 3. All-Display Stitch
+
+- [ ] Menu: Capture All Displays β stitched Capture opens in Editor
+- [ ] Multi-monitor: all displays visible in stitch (if applicable)
+- [ ] Error alert shown if stitch fails (simulate by disconnecting display mid-capture if possible)
+
+**Notes:** _______________
+
+---
+
+## 4. Scroll Capture
+
+- [ ] Menu: Scroll Capture β Overlay with scroll mode
+- [ ] Drag tall region β initial frame captured
+- [ ] Scroll source content, menu: Add Scroll Frame β frame count increments
+- [ ] Menu: Finish Scroll Capture β stitched tall Capture in Editor
+- [ ] Cancel scroll capture from menu β no orphan Editor
+- [ ] Overlap validation error surfaced on bad frames
+
+**Notes:** _______________
+
+---
+
+## 5. All 14 Tools + Undo/Redo
+
+Tools: Select, Arrow, Rectangle, Ellipse, Text, Pencil, Censor, Number, Stamp, Highlighter, Measure, Spotlight, Loupe, Eyedropper
+
+- [ ] Each Tool creates an Annotation
+- [ ] Select: move and resize handles work
+- [ ] Arrow: all 5 styles via toolbar
+- [ ] Censor: Blur, Pixelate, Solid, Erase modes
+- [ ] βZ undoes last annotation change
+- [ ] β§βZ redoes
+- [ ] Adjustments changes are **not** in undo stack
+- [ ] Beautify changes are **not** in undo stack
+
+**Notes:** _______________
+
+---
+
+## 6. Adjustments + Beautify + Brand Kits
+
+- [ ] Adjustments panel: change exposure/contrast β Canvas updates
+- [ ] Preset applies correctly
+- [ ] Reset restores neutral
+- [ ] Beautify: enable gradient, padding, radius, shadow, chrome
+- [ ] Export matches Beautify preview
+- [ ] Save brand kit β persists after Editor close
+- [ ] Load brand kit from panel
+
+**Notes:** _______________
+
+---
+
+## 7. Vision (OCR / QR / Face / PII / Translation)
+
+- [ ] Inspect Capture β Recognize Text β OCR results appear
+- [ ] QR code in Capture β detected and readable
+- [ ] Face in Capture β face regions detected
+- [ ] Censor Detected Sensitive Text β PII regions censored
+- [ ] Censor Detected Faces β face blur censors added
+- [ ] Translate (macOS 15+) β on-device translation in Vision panel
+- [ ] No network traffic during Vision (except optional Upload)
+
+**Notes:** _______________
+
+---
+
+## 8. History Restore
+
+- [ ] Open Editor β entry auto-created in History
+- [ ] Add annotations, close Editor
+- [ ] Menu: Capture History β entry listed with thumbnail
+- [ ] Open entry β annotations, adjustments, beautify, output format restored
+- [ ] Delete entry β removed from list
+- [ ] Clear All β confirmation, list empty
+
+**Notes:** _______________
+
+---
+
+## 9. Recording β Trim β Export
+
+- [ ] Menu: Start Recording β save panel, recording begins
+- [ ] Menu bar shows recording state / elapsed
+- [ ] Stop Recording β trim Editor opens
+- [ ] Trim in/out points β preview updates
+- [ ] Export MP4 β plays with audio
+- [ ] Export GIF β animates
+- [ ] Microphone captured on macOS 15+ (if enabled in system)
+
+**Notes:** _______________
+
+---
+
+## 10. Supabase Upload (Optional)
+
+- [ ] Preferences: project URL, anon key, bucket configured
+- [ ] Editor: Upload β progress shown
+- [ ] Link copied to clipboard β URL opens in browser
+- [ ] Upload disabled when not configured
+
+**Notes:** _______________
+
+---
+
+## 11. Hotkey Rebind
+
+- [ ] Preferences: record new shortcut (e.g. ββ§P)
+- [ ] New shortcut triggers Capture
+- [ ] Quit and relaunch β shortcut still works
+
+**Notes:** _______________
+
+---
+
+## 12. Permission Edge Cases
+
+- [ ] Fresh install / revoke permission β Capture opens Preferences
+- [ ] Recording without permission β opens Preferences (not generic alert only)
+- [ ] Grant permission β Quit & Reopen β Capture works
+- [ ] First-run onboarding shown once, skipped on relaunch
+
+**Notes:** _______________
+
+---
+
+## macOS 13β14 Fallback (VM / secondary machine)
+
+- [ ] Capture uses SCStream fallback (not SCScreenshotManager)
+- [ ] Recording uses LegacyRecordingWriter (not SCRecordingOutput)
+- [ ] Core flows 1, 5, 8 pass on macOS 13
+
+**Notes:** _______________
+
+---
+
+## Release Hardening
+
+- [ ] Sandbox enabled β Capture, recording, save panel, history all work
+- [ ] Gatekeeper: notarized zip opens without right-click workaround
+- [ ] Sparkle: Check for Updates finds appcast (no crash if offline)
+
+**Notes:** _______________
+
+---
+
+## Sign-off
+
+| Result | Count |
+|--------|-------|
+| Pass | ___ / 12 core + ___ release |
+| Fail | ___ |
+| Blocked | ___ |
+
+**Ship recommendation:** β Ready β Fix blockers β Defer macOS 13 claim
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..a507fd5
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,41 @@
+# Documentation
+
+Everything you need to build, verify, and ship Parcel.
+
+## Start here
+
+| | |
+|---|---|
+| [architecture.md](architecture.md) | Render pipeline, coordinate rules, module map β read before touching Editor or export |
+| [parity.md](parity.md) | Feature parity vs macshot reference β what's done, what's deferred |
+| [QA_CHECKLIST.md](QA_CHECKLIST.md) | Manual verification matrix for Capture, recording, Vision, upload |
+
+## Release & integrations
+
+| | |
+|---|---|
+| [integrations.md](integrations.md) | GitHub, Vercel, Supabase, Mobbin, Higgsfield wiring |
+| [mcp-setup.md](mcp-setup.md) | Cursor MCP configuration for connected services |
+| [MACOS13_VM_QA.md](MACOS13_VM_QA.md) | macOS 13 fallback paths (written but dev-machine untested) |
+
+## Agent / contributor guides
+
+| | |
+|---|---|
+| [../AGENTS.md](../AGENTS.md) | Canonical glossary, hard constraints, project layout |
+| [../CONTRIBUTING.md](../CONTRIBUTING.md) | Setup, build, PR expectations |
+
+## Glossary (quick reference)
+
+Use these exact terms in code and UI β no synonyms:
+
+| Term | Meaning |
+|------|---------|
+| **Capture** | Grabbing pixels + the resulting full-resolution image |
+| **Selection** | User-chosen region within a Capture |
+| **Overlay** | Full-screen selection UI (`NSPanel` + SwiftUI) |
+| **Editor** | Mark-up window for copy/save/upload |
+| **Annotation** | One mark-up object (Arrow, Censor, Text, β¦) |
+| **Tool** | Active annotation mode |
+| **Canvas** | Capture + Annotations drawing surface |
+| **Layer** | Z-ordered Annotation stack |
diff --git a/docs/architecture.md b/docs/architecture.md
new file mode 100644
index 0000000..06add14
--- /dev/null
+++ b/docs/architecture.md
@@ -0,0 +1,47 @@
+# Parcel architecture
+
+Parcel is a menu bar macOS app: **Capture β Overlay Selection β Editor β copy/save/upload**.
+
+## Render pipeline (do not regress)
+
+```
+raw Capture
+ β adjusted base (Core Image; neutral short-circuits)
+ β censor blur/pixelate derived from adjusted base
+ β AnnotatedCanvas (spotlight dim, vectors, censors)
+ β BeautifyCanvas wrapper
+ β ImageRenderer at capture.scale
+```
+
+Display and export share one composition (**on screen === saved**).
+
+## Coordinate rule
+
+```
+capturePoint = viewPoint / scale β innerOrigin
+innerOrigin = effectiveSettings.innerOrigin = (padding, padding + chromeHeight)
+```
+
+Overlays use the inverse: `+ innerOrigin Β· scale` on `.position()`.
+
+## Module map
+
+| Area | Role |
+|------|------|
+| `App/` | Coordinator, lifecycle, window wiring |
+| `Hotkeys/` | Carbon global hotkey + Preferences storage |
+| `Capture/` | ScreenCaptureKit freeze, Overlay, scroll stitch |
+| `Editor/` | Tools, Canvas, Beautify, Adjustments, export |
+| `History/` | Disk-backed re-editable documents |
+| `Recording/` | MP4 + trim + GIF |
+| `Vision/` | On-device OCR, faces, QR, translation |
+| `Upload/` | Supabase Storage REST client |
+| `Preferences/` | TCC guidance, hotkey recorder, upload config |
+
+## Undo scope
+
+βZ covers **annotation content only** (create, delete, move, resize, restyle, layer order, rotation). Adjustments and Beautify are document settings with panel Resets.
+
+## Sandbox
+
+Dev builds are non-sandboxed for TCC stability during iteration. See AGENTS.md for release hardening checklist.
diff --git a/docs/integrations.md b/docs/integrations.md
new file mode 100644
index 0000000..aced111
--- /dev/null
+++ b/docs/integrations.md
@@ -0,0 +1,48 @@
+# Connected services β Parcel / notable repo
+
+Repo: [github.com/bswxyz/notable](https://github.com/bswxyz/notable)
+
+| Service | Purpose | Where configured |
+|---------|---------|------------------|
+| **GitHub** | Source, Releases, CI | `.github/workflows/` |
+| **Vercel** | Marketing site `parcel.parable.dev` | `Website/vercel.json`, connect repo root `Website/` |
+| **Supabase** | Optional Capture upload (Storage) | Parcel β Preferences β Upload |
+| **Mobbin** | UI research MCP | `.cursor/mcp.json` |
+| **Higgsfield** | Icon / marketing assets MCP | `.cursor/mcp.json` |
+| **Parable** | Component registry MCP | Cursor MCP catalog |
+| **Sparkle** | In-app updates | `Website/public/appcast.xml`, `Scripts/release.sh` |
+
+## GitHub
+
+- **Default branch:** `master`
+- **Release:** tag `v*` β `release.yml` builds signed `Parcel.zip` + website artifact
+- **CI:** push/PR β `build.yml` compiles Parcel Debug
+
+Set repository **About β Website** to `https://parcel.parable.dev`.
+
+## Vercel
+
+1. Import `bswxyz/notable` in Vercel.
+2. Set **Root Directory** to `Website`.
+3. Framework preset: **Next.js** (auto-detected; static export to `out/`).
+4. Custom domain: `parcel.parable.dev`.
+
+## Supabase
+
+1. Create a **public** Storage bucket (e.g. `captures`).
+2. In Parcel: **Preferences β Upload** β paste project URL, anon key, bucket.
+3. Optional: set **Public base URL** to your CDN or `https://.supabase.co/storage/v1/object/public/`.
+
+Never commit anon keys. Use Cursor **Supabase MCP** (authenticate in Settings β MCP) to inspect buckets from the agent.
+
+## MCP (Cursor)
+
+See [mcp-setup.md](mcp-setup.md). Mobbin and Higgsfield are configured in `.cursor/mcp.json`.
+
+## App identity
+
+| | Value |
+|---|--------|
+| Display name | Parcel |
+| Bundle ID | `dev.parable.Parcel` |
+| Legacy (Notable) | Migrated automatically via `AppIdentity.migrateFromNotableIfNeeded()` |
diff --git a/docs/mcp-setup.md b/docs/mcp-setup.md
new file mode 100644
index 0000000..ec4a589
--- /dev/null
+++ b/docs/mcp-setup.md
@@ -0,0 +1,35 @@
+# MCP setup for design research
+
+## Parable (connected)
+
+Use the `user-parable` MCP in Cursor to list components and templates:
+
+- Hero: `dither-aurora`, `hero-section`
+- Blocks: `feature-bento`, `device-frame`, `faq-accordion`, `cta-banner`
+- Install: `npx shadcn@latest add https://parable-three.vercel.app/r/.json`
+
+The marketing site uses Parable-inspired Aurora + bento patterns in Astro.
+
+## Mobbin (connected)
+
+Configured in `~/.cursor/mcp.json` and `.cursor/mcp.json`:
+
+```json
+"mobbin": {
+ "type": "http",
+ "url": "https://api.mobbin.com/mcp",
+ "headers": {}
+}
+```
+
+OAuth via Cursor β Settings β Tools & MCP β **Connect** on Mobbin. Use for macOS screenshot app UI references.
+
+## Higgsfield (connected)
+
+```json
+"higgsfield": {
+ "url": "https://mcp.higgsfield.ai/mcp"
+}
+```
+
+OAuth on first generation request. Use for app icon, OG stills, and lifestyle marketing images.
diff --git a/docs/parity.md b/docs/parity.md
new file mode 100644
index 0000000..a214c97
--- /dev/null
+++ b/docs/parity.md
@@ -0,0 +1,52 @@
+# macshot vs Parcel β Feature Parity
+
+Working checklist for beating [macshot](https://macshot.io/) while keeping Parcel's local-first privacy story.
+
+| Capability | macshot | Parcel | Status | Owner |
+|---|---|---|---|---|
+| Region / window capture | Yes | Yes | Done | β |
+| Global hotkey | Configurable | Configurable (Preferences) | Done | App |
+| Ellipse annotation | Yes | Yes | Done | App |
+| Arrow Γ5 styles | Yes | Yes | Done | β |
+| Censor blur / pixelate / solid | Yes | Yes | Done | β |
+| Smart erase censor | Yes | Yes | Done | App |
+| Click-to-edit annotations | Yes | Yes | Done | β |
+| Layer z-order controls | Yes | Yes | Done | App |
+| Annotation rotation | Yes | Yes | Done | App |
+| Undo / redo | Yes | Yes (annotations) | Done | β |
+| Scroll capture | Overlay + preview | Overlay + menu bar | Done | App |
+| MP4 / GIF recording + trim | Yes | Yes | Done | β |
+| Recording fps presets | Up to 120fps | 30 / 60 / 120 | Done | App |
+| Beautify (30 gradients) | Yes | Yes | Done | β |
+| Brand kits | Partial | Yes | **Better** | β |
+| OCR | Yes (Vision) | Yes (Vision) | Done | β |
+| OCR translate | Cloud / Google | On-device (macOS 15+) | Done | App |
+| PII / face censor | Yes | Yes (local regex + Vision) | Done | β |
+| Cloud upload | Drive / imgbb / S3 | Supabase Storage | Done | App |
+| Re-editable history | Yes | Yes (disk-backed) | **Better** | β |
+| HEIC / TIFF export | Partial | Yes | **Better** | β |
+| Configurable hotkeys UI | Yes | Yes | Done | App |
+| Homebrew install | Yes | Cask in repo | Done | Repo |
+| Marketing site | macshot.io | Astro + Parable | Done | Web |
+| 40-language app UI | Yes | English (Phase 2) | Deferred | β |
+| Network LLM / AI search | Google AI | **Never** (hard constraint) | N/A | β |
+| iOS companion | No | No | Deferred | β |
+
+## Parcel differentiators (market harder than macshot)
+
+1. **No network AI** β Vision, OCR, translation, and redaction stay on-device.
+2. **Re-editable local History** β full annotation + adjustment + beautify state restored.
+3. **Beautify brand kits** β named presets saved locally.
+4. **Honest UX** β no fake upload buttons; Supabase only when configured.
+5. **Native SwiftUI** β no Electron, no web views in the app shell.
+
+## Verification
+
+- [ ] Capture region, window snap, all-display stitch
+- [ ] Every Tool creates and exports correctly (including Ellipse, erase Censor)
+- [ ] Layer order + rotation on selected Annotation
+- [ ] Hotkey change in Preferences persists across relaunch
+- [ ] Scroll capture from Overlay + menu bar finish flow
+- [ ] Supabase upload copies URL to clipboard
+- [ ] On-device translation (macOS 15+) in Vision panel
+- [ ] Website deploys to static host with download link
diff --git a/project.yml b/project.yml
index dfeb58e..bd86993 100644
--- a/project.yml
+++ b/project.yml
@@ -1,15 +1,20 @@
-name: Notable
+name: Parcel
options:
- bundleIdPrefix: io.notable
+ bundleIdPrefix: dev.parable
deploymentTarget:
macOS: "13.0"
createIntermediateGroups: true
groupSortPosition: top
generateEmptyDirectories: true
+packages:
+ Sparkle:
+ url: https://github.com/sparkle-project/Sparkle
+ from: "2.6.4"
+
settings:
base:
- MARKETING_VERSION: "0.1.0"
+ MARKETING_VERSION: "1.0.0"
CURRENT_PROJECT_VERSION: "1"
SWIFT_VERSION: "5.0"
CODE_SIGN_STYLE: Automatic
@@ -20,24 +25,39 @@ settings:
CLANG_ENABLE_OBJC_ARC: YES
targets:
- Notable:
+ Parcel:
type: application
platform: macOS
deploymentTarget: "13.0"
sources:
- - path: Sources/Notable
+ - path: Sources/Parcel
excludes:
- "Resources/Info.plist"
- - "Resources/Notable.entitlements"
+ - "Resources/Parcel.entitlements"
+ dependencies:
+ - package: Sparkle
+ product: Sparkle
settings:
base:
- PRODUCT_NAME: Notable
- PRODUCT_BUNDLE_IDENTIFIER: io.notable.Notable
- INFOPLIST_FILE: Sources/Notable/Resources/Info.plist
- CODE_SIGN_ENTITLEMENTS: Sources/Notable/Resources/Notable.entitlements
+ PRODUCT_NAME: Parcel
+ PRODUCT_BUNDLE_IDENTIFIER: dev.parable.Parcel
+ INFOPLIST_FILE: Sources/Parcel/Resources/Info.plist
+ CODE_SIGN_ENTITLEMENTS: Sources/Parcel/Resources/Parcel.entitlements
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
COMBINE_HIDPI_IMAGES: YES
ENABLE_HARDENED_RUNTIME: YES
LD_RUNPATH_SEARCH_PATHS:
- "@executable_path/../Frameworks"
+ OTHER_LDFLAGS:
+ - "-framework Sparkle"
+
+schemes:
+ Parcel:
+ build:
+ targets:
+ Parcel: all
+ run:
+ config: Debug
+ archive:
+ config: Release