diff --git a/.github/release-notes-header.md b/.github/release-notes-header.md index e73316b..8bce24a 100644 --- a/.github/release-notes-header.md +++ b/.github/release-notes-header.md @@ -4,12 +4,10 @@ Floatick 目前仍处于早期版本。第一次打开时,如果 macOS 需要 尝试打开一次 Floatick,然后前往“系统设置”→“隐私与安全”,在“安全性”区域 点击“仍要打开”。 -完成一次确认后,后续即可正常启动。应用内更新会使用独立的 Sparkle EdDSA -签名验证下载内容,并在安装前征求确认。 +完成一次确认后,后续即可正常启动。安装包同时支持 Apple 芯片和 Intel Mac。 Floatick is still at an early stage. If macOS asks for an additional confirmation on first launch, try opening Floatick once, then go to System Settings → Privacy & Security and choose Open Anyway in the Security section. -In-app updates verify downloads with an independent Sparkle EdDSA signature and -always ask before installation. +The same installer supports both Apple silicon and Intel Macs. diff --git a/.github/release-notes/0.4.0.md b/.github/release-notes/0.4.0.md new file mode 100644 index 0000000..ccd9262 --- /dev/null +++ b/.github/release-notes/0.4.0.md @@ -0,0 +1,26 @@ +## Floatick v0.4.0 + +### 中文 + +- 全新菜单栏应用:点击托盘图标打开待办和笔记,角标显示未完成任务数。 +- 使用 Tauri、React 与 Rust 重构,继续使用 `~/.floatick` 保存本地数据。 +- 新版富文本编辑器支持阅读与编辑切换、任务清单、标题、Markdown 快捷输入和复制。 +- 待办和笔记共用彩色标签,支持搜索、组合筛选、归档和恢复。 +- 修复托盘点击与失焦关闭之间的竞争,减少面板打开时闪关、重开的情况。 +- 更新中英文官网、产品截图和手机布局,清理截图圆角白边。 + +升级说明:v0.4.0 会展示截止时间与逾期状态,但暂未迁移 v0.3.4 的到点弹出提醒和内置 +“检查更新”。后续版本可从官网或 GitHub Releases 下载安装。建议升级前备份 `~/.floatick`。 + +### English + +- A new menu bar app for todos and notes, with an active task count in the tray. +- Rebuilt with Tauri, React, and Rust; local data remains in `~/.floatick`. +- A new editor with reading and editing modes, checklists, headings, Markdown shortcuts, and copy. +- Shared colored tags, search, combined filters, archiving, and restore for todos and notes. +- Fixed competing tray and blur events that could make the panel briefly close and reopen. +- Refreshed the bilingual website, product screenshots, mobile layout, and screenshot corners. + +Upgrade note: v0.4.0 displays due dates and overdue status, but the v0.3.4 reminder popups and built-in +update checker have not yet been migrated. Download subsequent versions from the website or GitHub +Releases. Back up `~/.floatick` before upgrading. diff --git a/.github/workflows/candidate.yml b/.github/workflows/candidate.yml index 3f50c42..ccfdd45 100644 --- a/.github/workflows/candidate.yml +++ b/.github/workflows/candidate.yml @@ -14,18 +14,17 @@ concurrency: cancel-in-progress: true env: - FLUTTER_VERSION: 3.44.7 PRODUCT_NAME: Floatick jobs: candidate: name: Build draft release - runs-on: macos-15 + runs-on: macos-latest timeout-minutes: 45 steps: - name: Check out repository - uses: actions/checkout@v7 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -40,21 +39,23 @@ jobs: fi branch_version="${BASH_REMATCH[1]}" - full_version=$(awk '/^version:/{print $2}' pubspec.yaml) - app_version=${full_version%%+*} - app_build_number=${full_version#*+} + pkg_version=$(jq -r .version package.json) + tauri_version=$(jq -r .version src-tauri/tauri.conf.json) - if [[ "$full_version" == "$app_version" || - ! "$app_build_number" =~ ^[1-9][0-9]*$ ]]; then - echo "pubspec version must include a positive build number." >&2 + cargo_version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' src-tauri/Cargo.toml | head -n 1) + app_build_number=$(jq -r .bundle.macOS.bundleVersion src-tauri/tauri.conf.json) + if [[ "$pkg_version" != "$branch_version" || "$tauri_version" != "$branch_version" || "$cargo_version" != "$branch_version" ]]; then + echo "Branch release/$branch_version does not match version in package.json ($pkg_version) or tauri.conf.json ($tauri_version)." >&2 exit 1 fi - if [[ "$app_version" != "$branch_version" ]]; then - echo "Branch release/$branch_version does not match pubspec version $app_version." >&2 + if [[ ! "$app_build_number" =~ ^[1-9][0-9]*$ ]]; then + echo "Set a positive bundle.macOS.bundleVersion build number." >&2 exit 1 fi - if git ls-remote --exit-code --tags origin "refs/tags/v$app_version" \ - >/dev/null 2>&1; then + + app_version="$branch_version" + + if git ls-remote --exit-code --tags origin "refs/tags/v$app_version" >/dev/null 2>&1; then echo "Stable tag v$app_version already exists." >&2 exit 1 fi @@ -63,69 +64,64 @@ jobs: echo "app_build_number=$app_build_number" >> "$GITHUB_OUTPUT" echo "candidate_tag=candidate/v$app_version" >> "$GITHUB_OUTPUT" - - name: Install Flutter - shell: bash - run: | - git clone \ - --branch "$FLUTTER_VERSION" \ - --depth 1 \ - https://github.com/flutter/flutter.git \ - "$RUNNER_TEMP/flutter" - echo "$RUNNER_TEMP/flutter/bin" >> "$GITHUB_PATH" + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-darwin,x86_64-apple-darwin + + - name: Cache cargo registry & target + uses: swatinem/rust-cache@v2 + with: + workspaces: src-tauri -> target - name: Install dependencies - run: flutter pub get + run: pnpm install --frozen-lockfile - - name: Verify source + - name: Verify frontend and backend tests run: | - dart format --output=none --set-exit-if-changed lib test - flutter analyze - flutter test + pnpm build + cargo test --manifest-path src-tauri/Cargo.toml --locked - - name: Build release app + - name: Build macOS release bundles + run: pnpm tauri build --target universal-apple-darwin --bundles app,dmg + + - name: Verify app version, architectures, and startup env: APP_VERSION: ${{ steps.version.outputs.app_version }} APP_BUILD_NUMBER: ${{ steps.version.outputs.app_build_number }} - run: | - flutter build macos \ - --release \ - --build-name "$APP_VERSION" \ - --build-number "$APP_BUILD_NUMBER" - - - name: Prepare unsigned candidate app - run: | - tool/release/prepare_unsigned_app.sh \ - "build/macos/Build/Products/Release/$PRODUCT_NAME.app" - - - name: Verify update feed shell: bash run: | - repository_name=${GITHUB_REPOSITORY#*/} - expected_feed_url="https://$GITHUB_REPOSITORY_OWNER.github.io/$repository_name/appcast.xml" - tool/release/verify_update_feed.sh \ - "build/macos/Build/Products/Release/$PRODUCT_NAME.app" \ - "$expected_feed_url" - - - name: Verify app architectures - shell: bash - run: | - app_path="build/macos/Build/Products/Release/$PRODUCT_NAME.app" - executable_name=$(/usr/libexec/PlistBuddy \ - -c 'Print :CFBundleExecutable' \ - "$app_path/Contents/Info.plist") - architectures=$(lipo -archs "$app_path/Contents/MacOS/$executable_name") - - if [[ "$architectures" != *arm64* || "$architectures" != *x86_64* ]]; then - echo "Expected a universal app, found: $architectures" >&2 + app_path="src-tauri/target/universal-apple-darwin/release/bundle/macos/$PRODUCT_NAME.app" + info_plist="$app_path/Contents/Info.plist" + installed_version=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$info_plist") + installed_build=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$info_plist") + executable_name=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$info_plist") + executable="$app_path/Contents/MacOS/$executable_name" + [[ "$installed_version" == "$APP_VERSION" && "$installed_build" == "$APP_BUILD_NUMBER" ]] + lipo -verify_arch arm64 x86_64 "$executable" + "$executable" > "$RUNNER_TEMP/floatick-startup.log" 2>&1 & + app_pid=$! + trap 'kill "$app_pid" 2>/dev/null || true' EXIT + sleep 5 + if ! kill -0 "$app_pid" 2>/dev/null; then + cat "$RUNNER_TEMP/floatick-startup.log" + echo "Floatick exited during the startup smoke test." >&2 exit 1 fi + kill "$app_pid" + wait "$app_pid" || true + trap - EXIT - - name: Smoke-test candidate app - run: | - tool/release/smoke_test_app.sh \ - "build/macos/Build/Products/Release/$PRODUCT_NAME.app" - - - name: Create candidate package + - name: Package candidate artifacts id: package env: APP_VERSION: ${{ steps.version.outputs.app_version }} @@ -133,21 +129,26 @@ jobs: shell: bash run: | artifact_directory="$RUNNER_TEMP/artifacts" + mkdir -p "$artifact_directory" + + dmg_source=$(find src-tauri/target/universal-apple-darwin/release/bundle/dmg -name "*.dmg" | head -n 1) + if [[ -z "$dmg_source" || ! -f "$dmg_source" ]]; then + echo "No DMG generated by Tauri build." >&2 + exit 1 + fi + dmg_name="$PRODUCT_NAME-$APP_VERSION-macos-universal.dmg" dmg_path="$artifact_directory/$dmg_name" - manifest_name="$PRODUCT_NAME-$APP_VERSION-release-manifest.json" - manifest_path="$artifact_directory/$manifest_name" - - tool/release/create_dmg.sh \ - "build/macos/Build/Products/Release/$PRODUCT_NAME.app" \ - "$dmg_path" \ - "$PRODUCT_NAME" + cp "$dmg_source" "$dmg_path" ( cd "$artifact_directory" shasum -a 256 "$dmg_name" > "$dmg_name.sha256" ) dmg_sha256=$(awk '{print $1}' "$dmg_path.sha256") + + manifest_name="$PRODUCT_NAME-$APP_VERSION-release-manifest.json" + manifest_path="$artifact_directory/$manifest_name" jq -n \ --arg product "$PRODUCT_NAME" \ --arg version "$APP_VERSION" \ @@ -199,8 +200,7 @@ jobs: gh release edit "$CANDIDATE_TAG" \ --draft \ --target "$GITHUB_SHA" \ - --title "$PRODUCT_NAME v$APP_VERSION — Candidate" \ - --notes-file .github/release-notes-header.md + --title "$PRODUCT_NAME v$APP_VERSION — Candidate" gh release upload "$CANDIDATE_TAG" \ "$DMG_PATH#$DMG_NAME" \ "$DMG_PATH.sha256#$DMG_NAME.sha256" \ @@ -214,8 +214,7 @@ jobs: --verify-tag \ --draft \ --target "$GITHUB_SHA" \ - --title "$PRODUCT_NAME v$APP_VERSION — Candidate" \ - --notes-file .github/release-notes-header.md + --title "$PRODUCT_NAME v$APP_VERSION — Candidate" fi echo "Draft release updated for commit $GITHUB_SHA." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb8b22..a066618 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,9 @@ name: CI on: + push: + branches: + - main pull_request: branches: - main @@ -9,67 +12,102 @@ on: permissions: contents: read -env: - FLUTTER_VERSION: 3.44.7 - jobs: - test: + required-check: name: Analyze and test - runs-on: macos-15 - timeout-minutes: 35 + needs: [frontend, backend, tauri-build] + if: always() + runs-on: ubuntu-latest + steps: + - name: Require all build and test jobs to pass + env: + RESULTS: ${{ toJSON(needs) }} + run: echo "$RESULTS" | jq -e 'all(.[]; .result == "success")' + + frontend: + name: Frontend check & build + runs-on: macos-latest + timeout-minutes: 15 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Install root dependencies + run: pnpm install --frozen-lockfile + + - name: Build frontend (tsc & vite) + run: pnpm build + + - name: Install website dependencies + run: pnpm --dir website install + + - name: Build website (astro) + run: pnpm --dir website build + + backend: + name: Rust check & test + runs-on: macos-latest + timeout-minutes: 20 + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + + - name: Cache cargo registry & target + uses: swatinem/rust-cache@v2 + with: + workspaces: src-tauri -> target + + - name: Cargo check + run: cargo check --manifest-path src-tauri/Cargo.toml + + - name: Cargo test + run: cargo test --manifest-path src-tauri/Cargo.toml + + tauri-build: + name: Tauri smoke build + needs: [frontend, backend] + runs-on: macos-latest + timeout-minutes: 25 steps: - name: Check out repository - uses: actions/checkout@v7 - - - name: Install Flutter - shell: bash - run: | - git clone \ - --branch "$FLUTTER_VERSION" \ - --depth 1 \ - https://github.com/flutter/flutter.git \ - "$RUNNER_TEMP/flutter" - echo "$RUNNER_TEMP/flutter/bin" >> "$GITHUB_PATH" + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'pnpm' + + - name: Set up Rust toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Cache cargo registry & target + uses: swatinem/rust-cache@v2 + with: + workspaces: src-tauri -> target - name: Install dependencies - run: flutter pub get - - - name: Verify formatting - run: dart format --output=none --set-exit-if-changed lib test integration_test - - - name: Analyze - run: flutter analyze - - - name: Test - run: flutter test - - - name: Run macOS UI automation - run: tool/test/run_ui_tests.sh - - - name: Build macOS release app - run: flutter build macos --release - - - name: Prepare unsigned test app - run: | - tool/release/prepare_unsigned_app.sh \ - build/macos/Build/Products/Release/Floatick.app - - - name: Verify app architectures - shell: bash - run: | - app_path="build/macos/Build/Products/Release/Floatick.app" - executable_name=$(/usr/libexec/PlistBuddy \ - -c 'Print :CFBundleExecutable' \ - "$app_path/Contents/Info.plist") - architectures=$(lipo -archs "$app_path/Contents/MacOS/$executable_name") - - if [[ "$architectures" != *arm64* || "$architectures" != *x86_64* ]]; then - echo "Expected a universal app, found: $architectures" >&2 - exit 1 - fi - - - name: Smoke-test release app - run: | - tool/release/smoke_test_app.sh \ - build/macos/Build/Products/Release/Floatick.app + run: pnpm install --frozen-lockfile + + - name: Build Tauri app (binary verification) + run: pnpm tauri build --no-bundle diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9bba2c7..1937af7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,18 +47,14 @@ jobs: fi app_version="${BASH_REMATCH[1]}" - full_version=$(awk '/^version:/{print $2}' pubspec.yaml) - pubspec_version=${full_version%%+*} - app_build_number=${full_version#*+} - if [[ "$full_version" == "$pubspec_version" || - ! "$app_build_number" =~ ^[1-9][0-9]*$ ]]; then - echo "pubspec version must include a positive build number." >&2 - exit 1 - fi - if [[ "$pubspec_version" != "$app_version" ]]; then - echo "Tag $GITHUB_REF_NAME does not match pubspec version $pubspec_version." >&2 + pkg_version=$(jq -r .version package.json) + tauri_version=$(jq -r .version src-tauri/tauri.conf.json) + cargo_version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' src-tauri/Cargo.toml | head -n 1) + if [[ "$pkg_version" != "$app_version" || "$tauri_version" != "$app_version" || "$cargo_version" != "$app_version" ]]; then + echo "Tag $GITHUB_REF_NAME does not match package.json ($pkg_version) or tauri.conf.json ($tauri_version)." >&2 exit 1 fi + app_build_number=$(jq -r .bundle.macOS.bundleVersion src-tauri/tauri.conf.json) release_commit=$(git rev-parse "${GITHUB_SHA}^{commit}") git fetch origin main @@ -182,13 +178,6 @@ jobs: echo "Expected a universal app, found: $architectures" >&2 exit 1 fi - - repository_name=${GITHUB_REPOSITORY#*/} - expected_feed_url="https://$GITHUB_REPOSITORY_OWNER.github.io/$repository_name/appcast.xml" - tool/release/verify_update_feed.sh \ - "$app_path" \ - "$expected_feed_url" - hdiutil detach "$mount_point" trap - EXIT @@ -210,6 +199,10 @@ jobs: { cat .github/release-notes-header.md printf '\n\n' + if [[ -f ".github/release-notes/$APP_VERSION.md" ]]; then + cat ".github/release-notes/$APP_VERSION.md" + printf '\n\n' + fi cat "$generated_notes" } > "$final_notes" @@ -331,7 +324,7 @@ jobs: CANDIDATE_TAG: ${{ needs.verify.outputs.candidate_tag }} shell: bash run: | - if git ls-remote --exit-code --tags origin \ + if git ls-remote --exit-code --tags "https://github.com/$GITHUB_REPOSITORY.git" \ "refs/tags/$CANDIDATE_TAG" >/dev/null 2>&1; then gh api \ --method DELETE \ diff --git a/.gitignore b/.gitignore index 270fc98..dd20b51 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ build/ coverage/ dist/ +node_modules/ +src-tauri/target/ *.dmg *.xcarchive .build/ diff --git a/README.md b/README.md index e79fba7..198daa6 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,21 @@
- Floatick floating workspace for macOS with Todo, Doing, deadlines, reminders, and quick notes -

Floatick — Floating Todos and Notes for macOS

-

An open-source, local-first macOS app for lightweight tasks and notes.

-

- Capture tasks and thoughts one click away—without an account or cloud service. -

+

+ Floatick Icon + Floatick +

+

Lightweight Menu Bar Todos & Notes for macOS

+

Capture fast. Finish with focus. Local-first · Fast & Minimal · Open Source

- CI status + CI Status - macOS 10.15 or later - Flutter 3.44.7 + macOS 10.15+ + Tauri v2 + Rust 1.80+ + React 19 + TypeScript 5.x - MIT license + MIT License

@@ -24,175 +23,111 @@

-Floatick is an offline-friendly desktop todo and quick notes app that rests above your -workspace as a small, draggable icon. Click it and the icon expands into a -focused panel; collapse it and the icon returns to the same anchor. The panel chooses -its expansion direction from the available screen space, so it stays useful -near any display edge. Tasks move through a deliberately small flow—Todo, -Doing, and Done—with optional deadlines and quiet in-app reminders. - -## A focused macOS space for tasks and notes - -- **Always within reach** — drag the floating icon anywhere, then click to open. -- **Fast task flow** — create, edit, complete, search, archive, restore, and - organize tasks in automatic daily sections. -- **Visible focus** — mark the task you are actively working on as Doing, give - it a calm visual emphasis, and filter the list to active work. -- **Deadlines without noise** — set a due time while creating or editing a todo, - or directly from the list; Floatick surfaces a compact in-app reminder and - keeps overdue tasks visible without relying on system notifications. -- **Lightweight quick notes** — capture ideas, logs, and snippets grouped by - date, with search, pinning, archiving, shared tags, autosave, and Markdown preview. -- **Shared organization** — reuse color-coded tags across todos and notes, - combine filters, and copy a complete todo as Markdown. -- **Local by default** — no account, cloud service, or telemetry. Your todo and - note data remains in `~/.floatick`. -- **Made for macOS** — transparent AppKit window behavior, keyboard shortcuts, - context-menu Quit, and support for Reduce Motion. -- **Comfortable in any workspace** — system, light, and dark themes with English - and Simplified Chinese. -- **Built-in updates** — automatic and manual update checks powered by Sparkle. - -## Download Floatick for macOS - -Download the latest DMG from -[GitHub Releases](https://github.com/lucaslus/floatick/releases), open it, -and drag Floatick into `Applications`. Release packages are universal binaries -for both Apple silicon and Intel Macs. - -### A quick first-launch note - -Floatick is still an early preview and current downloads are not yet signed and -notarized with an Apple Developer ID. macOS may ask for one extra confirmation -the first time you open it: - -1. Try to open Floatick once. -2. Open **System Settings → Privacy & Security**. -3. Find Floatick in the **Security** section and choose **Open Anyway**. -4. Confirm **Open**. - -This confirmation is normally needed only once. For more detail, see -[Apple's guide to opening an app from an unidentified developer](https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac). -Only download Floatick from this repository's Releases page. - -## Everyday use - -| Action | How | -| --- | --- | -| Reposition Floatick | Drag the floating icon | -| Open Floatick | Click the floating icon | -| Collapse the panel | Click the collapse button or press `Esc` | -| Create a todo | Press `⌘N`, or use the input at the top | -| Switch between todos and notes | Use the tabs at the top of the panel | -| Create a note | Open Notes and choose New | -| Search the current workspace | Press `⌘F` | -| Edit a todo | Hover over the item and choose Edit | -| Start or stop Doing | Hover over a todo and use its play control | -| Show only active work | Use the Doing filter beside search | -| Set or edit a deadline | Use the alarm control on a todo row or in the todo editor | -| Complete a todo | Select its checkbox | -| Archive or restore | Use the action at the end of the item | -| Quit Floatick | Right-click the floating icon and choose Quit | - -## Local data and privacy - -Floatick creates its working directory on first launch: - -| Path | Purpose | +

+ Floatick Menu Bar Todos, TipTap Markdown Editor, and Quick Notes +

+ +Floatick is a lightweight, local-first productivity app crafted natively for macOS. It rests quietly in your Menu Bar with a live counter badge for pending tasks. One click slides open a focused workspace directly beneath the icon. No heavy setups, no cloud sign-ups—just fast capture and effortless follow-through. + +## Key Features + +- **Menu Bar Native**: Sits quietly in your system status bar with a live pending badge. Click to open directly beneath the tray icon without cluttering your Dock. +- **Full-Width Cards & Floating Capsules**: Task titles span the full row width without premature truncation. Hovering smoothly reveals frosted action capsules (deadline, edit, copy markdown, delete). +- **Separated Browsing & Editing**: Check off tasks instantly in the list; open the full TipTap editor drawer on demand via double-click or `⌘N`. +- **TipTap Markdown & Slash Commands**: Press `/` for interactive checklists, headings, code blocks, and quotes with instant formatting. +- **Deadlines without Noise**: Set due dates directly on task rows. Overdue items stay clearly highlighted without intrusive notification popups. +- **Quick Notes & Shared Tags**: Capture snippets and thoughts grouped by date. Reusable color tags work across both todos and notes. +- **Local-First Privacy**: No accounts, no cloud sync, no tracking. All data is saved as plain, readable JSON in `~/.floatick`. +- **Fast, Lightweight & Native**: Built with Tauri 2 and Rust with a sub-30MB idle footprint, smooth 120Hz animations, and optional launch at login. + +## Download & Installation + +Download the latest DMG installer from [GitHub Releases](https://github.com/lucaslus/floatick/releases) and drag Floatick to your `Applications` folder. The installer is a Universal Binary supporting both Apple silicon (M-series) and Intel Macs. + +> **First-Launch Note for macOS**: +> If macOS displays an "unidentified developer" warning, open **System Settings → Privacy & Security**, scroll down to **Security**, and click **Open Anyway** (only required on first launch). + +## Shortcuts & Controls + +| Action | Shortcut / Gesture | Description | +| --- | --- | --- | +| **Toggle Window** | Click Menu Bar Icon | Toggle Floatick panel under the status tray | +| **New Item** | `⌘N` | Open editor drawer (Todo or Note) | +| **Edit Item** | Double-click / Hover ✏️ | Open TipTap editor drawer for existing item | +| **Save & Close** | `⌘ ⏎` (Cmd + Enter) | Save current item and close drawer | +| **Close / Dismiss** | `Esc` | Closes active drawer first, then collapses panel | +| **Search Workspace** | `⌘F` | Focus search bar | +| **Switch Tabs** | `Tab` / `⌘1`, `⌘2` | Toggle between Todos and Notes views | +| **Preferences** | `⌘,` | Open Settings drawer (Theme, autostart, pin) | +| **Slash Commands** | Type `/` in editor | Insert task lists, headings, code blocks, quotes | +| **Set Deadline** | Click ⏰ on hover | Open date & time picker directly on row | +| **Copy Markdown** | Click ⎘ on hover | Copy title and content as clean Markdown | +| **Quit Floatick** | Right-click tray / Settings | Quit the application | + +## Local Data & Privacy + +Floatick automatically creates human-readable JSON files in your home directory on first launch: + +| File Path | Purpose | | --- | --- | -| `~/.floatick/todos.json` | Todos, Doing/completion state, deadlines, reminders, and archive state | -| `~/.floatick/notes.json` | Notes, tags, pinning, and archive state | -| `~/.floatick/tags.json` | Reusable tags shared by todos and notes | -| `~/.floatick/settings.json` | Theme and language preferences | +| `~/.floatick/todos.json` | Todos, completion status, deadlines, reminders, and archives | +| `~/.floatick/notes.json` | Notes content, color tags, pinning, and archives | +| `~/.floatick/tags.json` | Reusable color tags shared between todos and notes | +| `~/.floatick/settings.json` | Theme (system/light/dark), language, autostart, and stay-on-top | -Sparkle stores the automatic-update preference in standard macOS application -preferences. Floatick does not require an account and does not upload your todo -or note data. Network access is used only to check for and download application updates. +Your data never leaves your computer. Floatick operates completely offline and collects zero telemetry. ## Development ### Requirements - macOS 10.15 or later -- Flutter `3.44.7` -- A complete Xcode installation +- [Node.js](https://nodejs.org/) 18+ and [pnpm](https://pnpm.io/) +- [Rust](https://www.rust-lang.org/) (1.80+) and Cargo -### Run locally +### Local Setup ```bash -flutter pub get -flutter run -d macos -``` +# 1. Install dependencies +pnpm install -If Flutter cannot find Xcode, finish Xcode's first-launch setup: - -```bash -sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer -sudo xcodebuild -runFirstLaunch +# 2. Start Tauri development server (with hot reload) +pnpm tauri:dev ``` -### Verify a change +### Production Build ```bash -dart format --output=none --set-exit-if-changed lib test integration_test -flutter analyze -flutter test -tool/test/run_ui_tests.sh -flutter build macos --release +# Build Universal Binary DMG and .app bundle +pnpm tauri:build ``` -The release app is written to -`build/macos/Build/Products/Release/Floatick.app`. -See the [testing guide](./docs/TESTING.md) for the automated user journeys and -the macOS system boundaries that remain in Draft acceptance. +The output files will be created in `src-tauri/target/release/bundle/macos/` and `.../dmg/`. -## Project structure +## Project Structure ```text -lib/ - app/ App composition and themes - core/ Shared platform, storage, and UI primitives - features/ Todo, notes, settings, and update features - l10n/ English and Simplified Chinese resources -macos/Runner/ AppKit window shell and Sparkle integration -test/ Repository, ViewModel, and widget tests -integration_test/ Real-engine macOS user journeys -tool/ Icon and release tooling -``` - -Flutter owns product UI and state. A small AppKit shell owns macOS-specific -window behavior and Sparkle. Product data never crosses the platform channel. -See [Architecture](./docs/ARCHITECTURE.md) for the dependency boundaries. - -## Development and release model - -```mermaid -flowchart LR - A["feature/* or fix/*"] --> B["Pull request"] - B --> C["main"] - C --> D["release/x.y.z"] - D --> E["Private Draft candidate"] - E --> F["Manual acceptance"] - F --> G["vX.Y.Z + production approval"] - G --> H["Public Release + Sparkle appcast"] +src/ # Frontend (React 19 + TypeScript + Vite + Tailwind CSS) + components/ + common/ # TipTap editor, search bar, common UI components + layout/ # Header, ContentSwitcher, window frame + notes/ # Note list, note rows, note editor drawer + settings/ # Preferences drawer (theme, language, autostart) + tags/ # Tag management & filtering + todos/ # Todo list, item rows, deadline picker, editor drawer + stores/ # Zustand state management + i18n/ # Localization resources (English and Chinese) + lib/ # Tauri IPC API bindings +src-tauri/ # Native Backend (Rust + Tauri 2.x) + src/ + tray.rs # macOS Menu Bar tray & magnetic window anchoring + storage.rs # Atomic JSON file persistence in ~/.floatick + commands.rs # IPC commands exposed to frontend + lib.rs # Window lifecycle & auto-dismiss on blur +website/ # Marketing website (Astro) +docs/ # Design specifications and architecture guides ``` -Daily work reaches `main` through pull requests. A `release/x.y.z` branch builds -a private Draft candidate for manual testing. A stable `vX.Y.Z` tag promotes -the exact accepted DMG after production approval; the release workflow does not -rebuild it. - -Read the [development and release workflow](./docs/RELEASING.md) before -preparing a release. - -## Contributing - -[Issues](https://github.com/lucaslus/floatick/issues) and focused pull -requests are welcome. Please keep changes scoped, add tests for meaningful -behavior, and preserve the local JSON data contract. - ## License -Floatick is available under the [MIT License](./LICENSE). - -© 2026 lucaslushuo +Floatick is released under the [MIT License](./LICENSE). diff --git a/README.zh-CN.md b/README.zh-CN.md index f19cebd..80a03fc 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,22 +1,21 @@
- Floatick macOS 悬浮工作区,展示 Todo、Doing、DDL、提醒与 Quick Notes -

Floatick — macOS 悬浮 Todo 与笔记

-

一款开源、本地优先的 macOS 轻量 Todo 与笔记应用。

-

- 无需账号或云服务,让任务和想法始终触手可及。 -

+

+ Floatick Icon + Floatick +

+

常驻 macOS 菜单栏的轻量待办与便签

+

随手记,专心做。本地存储 · 极简轻快 · 免费开源

CI 状态 - 支持 macOS 10.15 或更高版本 - Flutter 3.44.7 + macOS 10.15+ + Tauri v2 + Rust 1.80+ + React 19 + TypeScript 5.x - MIT 许可证 + MIT 许可证

@@ -24,165 +23,111 @@

-Floatick 是一款支持离线使用的 macOS 桌面 Todo 与 Quick Notes 应用。它平时以一个小巧、 -可拖动的图标悬浮在工作区上方;点击图标会展开专注的面板,收起后则会准确 -回到原来的锚点。面板还会根据图标附近的屏幕空间自动选择展开方向,因此放在 -屏幕边缘也能自然使用。任务只保留 Todo、Doing 和 Done 三个明确状态,并可按需 -设置 DDL 与克制的应用内提醒。 - -## 专注而轻量的 macOS Todo 与笔记空间 - -- **随时可用**——图标可以拖到任意位置,点击即可展开。 -- **完整待办流程**——支持创建、编辑、完成、搜索、归档和恢复,并按天自动分组。 -- **看得见的专注状态**——把当前任务标记为 Doing,用克制的视觉效果突出显示, - 也可以直接筛选所有进行中的任务。 -- **不打扰的 DDL**——新建、编辑或直接从列表设置截止时间;到点后显示紧凑的 - 应用内提醒,并持续标记逾期任务,不依赖系统通知。 -- **轻量 Quick Notes**——按日期记录灵感、日志和片段,支持搜索、置顶、归档、 - 共享标签、自动保存与 Markdown 预览。 -- **共享整理能力**——Todo 与笔记复用彩色标签、组合筛选,并支持完整复制 Markdown。 -- **默认仅存本地**——不需要账号、云服务或遥测,Todo 与笔记数据保存在 `~/.floatick`。 -- **贴合 macOS**——使用 AppKit 管理透明悬浮窗口,支持快捷键、右键退出和 - “减少动态效果”。 -- **适应不同工作环境**——支持跟随系统、浅色和深色主题,以及英文和简体中文。 -- **内置更新能力**——通过 Sparkle 支持自动检查和手动检查更新。 +

+ Floatick 菜单栏待办、TipTap 富文本写作与随手记便签 +

-## 下载 Floatick macOS 版 +Floatick 是一款专为 macOS 设计的原生轻量效率工具。安静常驻于屏幕顶部的菜单栏中,未完成事项实时显示角标;点击图标即在手边展开浮动面板。无需复杂配置,点开即记,做完即勾,不扰专注。 -从 [GitHub Releases](https://github.com/lucaslus/floatick/releases) -下载最新 DMG,打开后将 Floatick 拖入 `Applications`。发布包是 Universal -Binary,同时支持 Apple 芯片和 Intel Mac。 +## 特性亮点 -### 首次启动的小提示 +- **原生常驻菜单栏**:常驻右上角状态栏,角标实时显示待办数,点击即开,不占 Dock。 +- **全宽卡片与悬浮胶囊**:长标题全宽展示无遮挡;鼠标悬浮呼出截止时间、编辑、复制与删除操作胶囊。 +- **浏览勾选与编辑抽屉独立**:列表专注纯粹浏览与单键勾销;双击或按 `⌘N` 唤出独立编辑抽屉。 +- **TipTap 富文本与斜杠指令**:输入 `/` 快速插入交互式任务清单、多级标题、代码块与引用。 +- **到点提醒与截止时间**:按需设置截止时间,临近与逾期状态清晰高亮,适度提醒不吵闹。 +- **便签与彩色标签**:随时速记碎片灵感;待办与便签共享彩色标签,支持快速多维度筛选。 +- **数据完全保存在本机**:无账号体系、无云端同步,数据以纯文本 JSON 格式存放在 `~/.floatick`。 +- **原生轻快省电**:基于 Tauri 2 与 Rust 构建,内存占用极低(通常低于 30MB),支持开机自启与置顶。 -Floatick 目前仍处于早期预览阶段,下载包暂未使用 Apple Developer ID 完成签名 -和公证。因此,macOS 第一次打开时可能需要额外确认一次: +## 下载安装 -1. 先尝试打开一次 Floatick。 -2. 打开**系统设置 → 隐私与安全**。 -3. 在**安全性**区域找到 Floatick,点击**仍要打开**。 -4. 再次确认**打开**。 +前往 [GitHub Releases](https://github.com/lucaslus/floatick/releases) 下载最新 DMG 安装包,打开后将 Floatick 拖入 `Applications` 即可。安装包为 Universal Binary,原生支持 Apple 芯片(M 系列)与 Intel 架构。 -这项确认通常只需要完成一次。更多说明可以查看 -[Apple 官方帮助](https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac)。 -请只从本仓库的 Releases 页面下载 Floatick。 +> **macOS 首次打开提示**: +> 如打开时提示“来自未受信任的开发者”,请前往 **系统设置 → 隐私与安全**,在“安全性”中点击 **仍要打开** 即可(仅需首次确认一次)。 -## 日常操作 +## 快捷键与常用交互 -| 操作 | 使用方式 | -| --- | --- | -| 调整位置 | 拖动悬浮图标 | -| 展开 Floatick | 点击悬浮图标 | -| 收起面板 | 点击收起按钮或按 `Esc` | -| 创建待办 | 按 `⌘N`,或使用顶部输入框 | -| 切换 Todo 与笔记 | 使用面板顶部的标签页 | -| 创建笔记 | 打开 Notes 并点击新建 | -| 搜索当前空间 | 按 `⌘F` | -| 编辑待办 | 将鼠标悬浮到待办上并点击编辑 | -| 开始或停止 Doing | 将鼠标悬浮到待办上并使用播放按钮 | -| 只查看进行中的任务 | 使用搜索框旁边的 Doing 筛选 | -| 设置或修改 DDL | 使用 Todo 行内的闹钟按钮,或在 Todo 编辑器中设置 | -| 完成待办 | 点击待办前的复选框 | -| 归档或恢复 | 使用待办末尾的操作按钮 | -| 退出 Floatick | 右键点击悬浮图标并选择退出 | +| 操作 | 快捷键 / 交互 | 说明 | +| --- | --- | --- | +| **唤出 / 收起** | 点击托盘图标 | 在托盘正下方弹出或收起面板 | +| **新建事项** | `⌘N` | 打开编辑抽屉(新建待办或便签) | +| **编辑事项** | 双击卡片 / 悬浮点击 ✏️ | 呼出 TipTap 编辑抽屉 | +| **保存退出** | `⌘ ⏎` | 保存当前内容并关闭抽屉 | +| **返回 / 收起** | `Esc` | 优先关闭抽屉,其次收起主面板 | +| **快速搜索** | `⌘F` | 聚焦搜索栏 | +| **切换标签** | `Tab` / `⌘1`, `⌘2` | 在待办与便签视图之间切换 | +| **偏好设置** | `⌘,` | 呼出设置抽屉(主题、开机自启、置顶等) | +| **斜杠排版** | 输入 `/` | 插入任务清单、标题、代码块等 | +| **设定截止时间** | 悬浮点击 ⏰ | 直接呼出时间选择器 | +| **复制 Markdown** | 悬浮点击 ⎘ | 将标题与正文一键复制为 Markdown | +| **退出应用** | 右键托盘 / 设置菜单 | 退出 Floatick | ## 本地数据与隐私 -Floatick 第一次启动时会自动创建工作目录: +Floatick 首次启动时会在用户目录自动创建数据文件: -| 路径 | 用途 | +| 文件路径 | 内容说明 | | --- | --- | -| `~/.floatick/todos.json` | 待办、Doing/完成状态、DDL、提醒与归档状态 | -| `~/.floatick/notes.json` | 笔记、标签、置顶与归档状态 | -| `~/.floatick/tags.json` | Todo 与笔记共享的可复用标签 | -| `~/.floatick/settings.json` | 主题与语言设置 | +| `~/.floatick/todos.json` | 待办列表、完成状态、截止时间、提醒与归档 | +| `~/.floatick/notes.json` | 便签正文、彩色标签、置顶与归档 | +| `~/.floatick/tags.json` | 待办与便签共享的可复用彩色标签 | +| `~/.floatick/settings.json` | 主题外观、界面语言、开机自启与置顶偏好 | -Sparkle 会将自动更新偏好保存在 macOS 标准应用偏好中。Floatick 不需要账号, -也不会上传 Todo 或笔记数据;网络访问仅用于检查和下载应用更新。 +所有数据完全保存在本机,不经过任何云端服务器,亦无任何数据遥测。 ## 本地开发 ### 环境要求 - macOS 10.15 或更高版本 -- Flutter `3.44.7` -- 完整安装的 Xcode +- [Node.js](https://nodejs.org/) 18+ 与 [pnpm](https://pnpm.io/) +- [Rust](https://www.rust-lang.org/) (1.80+) 与 Cargo 环境 -### 运行项目 +### 启动开发 ```bash -flutter pub get -flutter run -d macos -``` +# 1. 安装依赖 +pnpm install -如果 Flutter 无法找到 Xcode,请先完成 Xcode 的首次配置: - -```bash -sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer -sudo xcodebuild -runFirstLaunch +# 2. 启动开发模式(支持前端热重载) +pnpm tauri:dev ``` -### 验证改动 +### 生产打包 ```bash -dart format --output=none --set-exit-if-changed lib test integration_test -flutter analyze -flutter test -tool/test/run_ui_tests.sh -flutter build macos --release +# 构建 Universal Binary DMG 与 .app +pnpm tauri:build ``` -Release 应用位于 -`build/macos/Build/Products/Release/Floatick.app`。 -完整自动化用户链路和仍需 Draft 人工验收的 macOS 系统边界见 -[测试指南](./docs/TESTING.md)。 +产物生成于 `src-tauri/target/release/bundle/macos/` 与 `dmg/` 目录下。 ## 项目结构 ```text -lib/ - app/ 应用装配与主题 - core/ 共享的平台、存储和 UI 基元 - features/ Todo、笔记、设置和更新功能 - l10n/ 英文与简体中文资源 -macos/Runner/ AppKit 窗口外壳与 Sparkle 集成 -test/ Repository、ViewModel 和 Widget 测试 -integration_test/ 真实 macOS 引擎用户链路 -tool/ 图标与发布工具 +src/ # 前端界面 (React 19 + TypeScript + Vite + Tailwind CSS) + components/ + common/ # TipTap 富文本编辑器、搜索栏、通用组件 + layout/ # 顶栏 Header、切换器 ContentSwitcher、主浮窗容器 + notes/ # 便签列表、便签卡片、便签编辑抽屉 + settings/ # 偏好设置抽屉 (主题、语言、自启、置顶) + tags/ # 标签管理与筛选 + todos/ # 待办列表、事项卡片、截止时间选择器、待办编辑抽屉 + stores/ # Zustand 全局状态流 + i18n/ # 多语言资源 (zh / en) + lib/ # Tauri IPC 调用封装 +src-tauri/ # 原生后端 (Rust + Tauri 2.x) + src/ + tray.rs # macOS 菜单栏托盘与窗口磁吸定位 + storage.rs # 本地 JSON 原子持久化存储 + commands.rs # 暴露给前端调用的 Tauri Command + lib.rs # 窗口生命周期与失焦自动收起 +website/ # 官网工程 (Astro) +docs/ # 设计规范与架构文档 ``` -Flutter 负责产品 UI 和状态;轻量 AppKit 外壳负责 macOS 专属窗口行为与 -Sparkle。产品数据不会经过平台通道。依赖边界详见 -[架构说明](./docs/ARCHITECTURE.md)。 - -## 开发与发布模型 - -```mermaid -flowchart LR - A["feature/* 或 fix/*"] --> B["Pull Request"] - B --> C["main"] - C --> D["release/x.y.z"] - D --> E["私有 Draft 候选版本"] - E --> F["人工验收"] - F --> G["vX.Y.Z + production 审批"] - G --> H["公开 Release + Sparkle appcast"] -``` - -日常改动通过 Pull Request 进入 `main`。`release/x.y.z` 分支会生成用于 -人工测试的私有 Draft;验收通过后,对完全相同的候选提交打 `vX.Y.Z` 标签, -经过 production 审批后直接晋级已测试的 DMG,正式发布阶段不会重新构建。 - -准备发布前,请先阅读完整的 -[开发与发布流程](./docs/DEVELOPMENT_WORKFLOW.md)。 - -## 参与贡献 - -欢迎提交 [Issue](https://github.com/lucaslus/floatick/issues) 和范围清晰的 -Pull Request。请为有意义的行为变更补充测试,并保持本地 JSON 数据格式向后 -兼容。 - -## 许可证 - -Floatick 使用 [MIT License](./LICENSE)。 +## 开源协议 -© 2026 lucaslushuo +Floatick 基于 [MIT License](./LICENSE) 开源。 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index ac5950e..a7d261b 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,116 +1,104 @@ # Architecture -Floatick uses Flutter for product UI and state, and a small AppKit shell for -macOS-only window behavior. +Floatick is a native macOS Menu Bar productivity application built on Tauri v2, Rust, React 19, and Tailwind CSS. -## Dependency direction +## High-Level Architecture ```text -presentation -> domain -presentation -> data (injected through ViewModels) -data -> domain -app -> features + core -macOS shell <-> core/platform through MethodChannel +┌────────────────────────────────────────────────────────┐ +│ React 19 Frontend │ +│ │ +│ Components & UI State & Domain │ +│ ├── ActionBar ├── useTodoStore │ +│ ├── TodoList / TodoDrawer ├── useNoteStore │ +│ ├── NoteList / NoteDrawer ├── useTagStore │ +│ ├── FloatickTiptapEditor └── useSettingsStore │ +│ └── Settings / Tag Modals │ +└───────────────────────────┬────────────────────────────┘ + │ Tauri IPC (Commands & Events) +┌───────────────────────────▼────────────────────────────┐ +│ Rust Tauri Backend │ +│ │ +│ Window & Tray Management Storage Engine │ +│ ├── AppKit NSStatusBar ├── ~/.floatick/ │ +│ │ (Live badge counter) │ ├── todos.json │ +│ ├── Popover Positioning │ ├── notes.json │ +│ │ (Anchored under tray) │ ├── tags.json │ +│ └── Blur event listener │ └── settings.json │ +│ (Auto-hide on click) └── Atomic tempfile rename │ +└───────────────────────────┬────────────────────────────┘ ``` -Feature code must not import another feature's data layer. App assembly belongs -in `lib/app`; screen-level presentation may compose injected ViewModels from -multiple features without reaching into their data implementations. Reusable -platform and visual primitives belong in `lib/core`. - -## Directory layout +## Layers and Responsibilities + +### 1. Presentation Layer (React 19 & Tailwind CSS) +- **State Management**: Lightweight Zustand stores (`useTodoStore`, `useNoteStore`, `useTagStore`, `useSettingsStore`) handle in-memory reactive state, filtering (active, completed, archived, tag-based), and fast tag queries. +- **Rich Document Editing**: Built-in **TipTap Markdown Editor** (`FloatickTiptapEditor`) provides interactive checkbox task lists (`TaskList`), code blocks with syntax highlighting, blockquotes, headings, and slash command popup (`/task`, `/h1`-`/h3`, `/code`, `/quote`, `/divider`). +- **Keyboard-First Workflow**: Global keyboard shortcuts (`⌘N`, `⌘F`, `Esc`, `Tab`, `⌘,`, `Enter`) provide fluid interaction without lifting hands from the keyboard. +- **Internationalization**: Localized via `i18next` (`zh-CN` and `en-US`), hot-swappable in Settings. + +### 2. IPC & Bridge Layer (`src/services/tauri.ts`) +- Bridges the web view and the Rust core using strongly-typed Tauri v2 commands: + - `load_todos` / `save_todos`: Fetch and atomically persist todos. + - `load_notes` / `save_notes`: Fetch and atomically persist notes. + - `load_tags` / `save_tags`: Fetch and atomically persist user tags. + - `load_settings` / `save_settings`: Read and persist user preferences. + - `hide_window`: Programmatically close or collapse the window. + - `update_tray_badge`: Update the live pending task count in the macOS Menu Bar. + +### 3. Native & System Layer (Rust & AppKit) +- **Menu Bar Tray Item (`src-tauri/src/tray.rs`)**: + - Registered as a macOS `NSStatusBar` item with a template double-checkmark icon. + - Features dynamic badge text reflecting the count of uncompleted tasks. +- **Window Management (`src-tauri/src/window.rs`)**: + - Frameless, transparent, 440×700 popover window. + - Automatically calculates screen bounds and anchors directly beneath the tray icon. + - Listens to window blur (`on_window_event(WindowEvent::Focused(false))`) to automatically hide when clicking outside if `close_on_blur` is enabled. +- **Atomic Storage Engine (`src-tauri/src/commands/storage.rs`)**: + - Operates strictly in `~/.floatick/`. + - Implements atomic write semantics (write to `.tmp` file, sync, then rename) to eliminate any risk of file corruption during system shutdowns or crashes. + +## Directory Layout ```text -lib/ - app/ - floatick_app.dart - theme/ - l10n/ - app_en.arb - app_zh.arb - core/ - platform/ - storage/ - ui/ - features/ - notes/ - data/ - domain/ - presentation/ - settings/ - data/ - domain/ - presentation/ - todos/ - data/ - domain/ - presentation/ - widgets/ - updates/ - data/ - domain/ - presentation/ -test/ - app/ - features/ -macos/ - Runner/ -tool/ - release/ +src/ + components/ + common/ # TipTap editor, slash menu, ActionBar, TagBadge, etc. + notes/ # NoteList, NoteCard, NoteEditorDrawer + settings/ # SettingsModal + tags/ # TagManagerModal, TagFilterBar + todos/ # TodoList, TodoItem, TodoEditorDrawer, DeadlinePicker + hooks/ # Keyboard shortcuts, blur handlers, window controls + i18n/ # Locales (zh.json, en.json) + services/ # Tauri IPC invocation wrappers + stores/ # Zustand stores (useTodoStore, useNoteStore, etc.) + types/ # Domain models (Todo, Note, Tag, Settings) +src-tauri/ + src/ + commands/ # Rust IPC handlers (storage, settings, updates) + tray.rs # macOS Menu Bar tray extra & live badge + window.rs # Positioning, toggle, blur and window behaviors + lib.rs # Tauri app builder and plugin registration + main.rs # Desktop entry point + tauri.conf.json # Tauri v2 window and security configuration + Cargo.toml # Rust dependencies +website/ # Official Astro bilingual marketing website +docs/ # Technical documentation and guides +legacy/ # Archived Flutter implementation ``` -- `domain`: immutable app models and domain concepts. -- `data`: local persistence and serialization boundaries. -- `presentation`: Views and `ChangeNotifier` ViewModels. -- `core/platform`: shared typed wrappers around native window channels. -- `core/storage`: shared storage failure types used at repository boundaries. -- `l10n`: English source copy, Simplified Chinese translations, and generated - Flutter localization accessors. -- `macos/Runner`: transparent floating window behavior and the Sparkle update - service; todo and note data does not cross either platform channel. - -## State and persistence - -`TodoViewModel`, `NoteViewModel`, `SettingsViewModel`, and `UpdateViewModel` own -presentation state. Repositories own file I/O, JSON compatibility, or typed platform-channel -boundaries. Repositories are constructor-injected so state behavior can be -tested without touching the user's home directory or launching Sparkle. - -Todo data, note data, shared tags, and Floatick-owned interface settings only use `~/.floatick`. -Repositories create it on first load and never read or write another hidden -application directory. Sparkle owns its automatic-check preference in the -standard macOS application `UserDefaults`; Floatick does not duplicate that -preference in `settings.json`. - -Manual update checks probe the configured appcast URL before presenting -Sparkle. An unpublished first-release feed is mapped across the platform -channel to a typed, informational state in Settings; other connectivity -failures remain recoverable errors. Sparkle still owns appcast parsing, -signature validation, download, and installation once the feed is available. - -Writes are serialized by `TodoViewModel` and `NoteViewModel` to prevent -overlapping mutations from losing updates. A write failure leaves the last -persisted in-memory state unchanged and exposes a recoverable UI error. - -## Testing +## Persistence and Data Flow -- Repository tests cover local JSON parsing, compatibility, and failure paths. -- ViewModel tests cover mutations, serialization order, filtering, and sorting. -- Widget tests cover the main interaction path and theme/settings behavior. -- Pull requests compile a release-mode universal macOS app in addition to - running Flutter analysis and tests. -- `release/x.y.z` builds a private Draft Release; `vX.Y.Z` promotes the same - accepted DMG only when its commit is reachable from `main`. -- AppKit window behavior and Sparkle installation remain native integration - boundaries and should be smoke-tested on both Apple silicon and Intel macOS - before a stable release. +1. On application launch, React triggers `load_todos`, `load_notes`, `load_tags`, and `load_settings`. +2. Rust loads and parses JSON files from `~/.floatick/`. If files do not exist, sensible defaults are returned and initialized. +3. Mutations in React update the Zustand store immediately (optimistic UI update). +4. Store subscribers trigger asynchronous debounced saves through Tauri IPC. +5. Rust writes data to temporary files and atomically renames them to `todos.json`, `notes.json`, etc. +6. When todo completion status or count changes, `update_tray_badge` updates the macOS Menu Bar tray text. -## Architecture guardrails +## Architecture Guardrails -- Keep platform-specific window code out of Flutter Views. -- Keep file I/O out of widgets and ViewModels. -- Add a package only when the standard library or existing platform bridge - cannot meet the requirement cleanly. -- Split presentation widgets when they gain independent state, reuse, or test - value; do not create one-file-per-widget ceremony. -- Preserve the local JSON contract or ship an explicit migration. +- **Local-First & Privacy**: Floatick operates strictly locally without remote servers, user tracking, or telemetry. All user data resides in `~/.floatick/`. +- **Zero Heavy Background Processes**: Idle memory consumption should remain under ~40-60 MB thanks to Tauri v2 and Rust. +- **Fail-Safe Persistence**: File writes must always use atomic rename patterns. +- **Platform Integrity**: Window behavior must feel native to macOS (tray anchor, blur-to-dismiss, instant toggle). diff --git a/docs/DEVELOPMENT_WORKFLOW.md b/docs/DEVELOPMENT_WORKFLOW.md index 6971c74..6f309e2 100644 --- a/docs/DEVELOPMENT_WORKFLOW.md +++ b/docs/DEVELOPMENT_WORKFLOW.md @@ -60,14 +60,24 @@ git switch -c feature/short-description ### 2. 开发和本地验证 - 修改代码前先确认根因和影响范围。 -- 核心逻辑补单元测试;交互变更补 Widget 测试。 -- 优先运行与改动直接相关的测试。 -- 完整 UI 自动化使用 `tool/test/run_ui_tests.sh`,覆盖真实 macOS Flutter 引擎和 - AppKit 原生边界;详细范围见 [TESTING.md](TESTING.md)。 -- 本地需要观察 UI 时运行: +- 前端逻辑采用 React 19 + TypeScript + Zustand 状态管理与 TipTap 编辑器。 +- 本地启动开发环境运行: ```bash -flutter run -d macos +pnpm install +pnpm tauri:dev +``` + +- 静态类型与前端构建验证: + +```bash +pnpm build +``` + +- Rust 后端检查与单元测试: + +```bash +cd src-tauri && cargo test ``` ### 3. 提交并创建 PR @@ -80,12 +90,11 @@ git push -u origin feature/short-description PR 必须合入 `main`。PR CI 会执行: -1. Dart 格式检查; -2. `flutter analyze`; -3. `flutter test`; -4. macOS UI 自动化与 AppKit 原生边界测试; -5. macOS Release 构建与首次启动烟测; -6. `arm64` 和 `x86_64` 双架构检查。 +1. 前端 TypeScript 编译与代码格式检查; +2. Rust 编译与后端单元测试; +3. macOS Menu Bar 托盘与窗口定位边界验证; +4. macOS Release 构建 (`pnpm tauri:build`); +5. Universal 架构检查 (`arm64` 与 `x86_64`)。 CI 通过后才能合并。普通开发不直接推送 `main`。 @@ -103,11 +112,7 @@ git pull --ff-only git switch -c "release/$VERSION" ``` -`pubspec.yaml` 必须包含公开版本和递增的构建号: - -```yaml -version: X.Y.Z+N -``` +确保 `package.json`、`src-tauri/tauri.conf.json` 以及 `src-tauri/Cargo.toml` 中版本号已同步更新。 ### 2. 生成 Draft Release @@ -129,11 +134,13 @@ git push -u origin "release/$VERSION" 从 Draft 下载 DMG,至少检查: - 安装、首次启动和 Gatekeeper 提示; -- 悬浮图标拖动、展开、收起和右键退出; -- Todo 创建、编辑、完成、搜索、归档和恢复; +- macOS 菜单栏托盘图标点击展开、收起、角标数字动态更新与失焦自动隐藏; +- TipTap 富文本编辑、斜杠命令(`/task`、`/h1`-`/h3`、`/code` 等)与 Markdown 快捷输入; +- Todo 创建、进行中聚焦、完成、搜索、归档和恢复; +- Note 创建、富文本写作、按日期分组与置顶; - 中英文、深浅色主题与设置持久化; - `~/.floatick` 数据在重启后保持不变; -- 更新检查、CPU、内存和动画表现。 +- 极低 CPU 和内存占用(空闲约 40-60 MB)。 如果失败,直接在同一个 `release/x.y.z` 分支修复并推送。工作流会替换 Draft 中的候选文件。旧候选不能打正式标签。 @@ -162,7 +169,7 @@ git push origin "v$VERSION" 正式工作流会验证: -- 标签、`pubspec.yaml` 和应用版本一致; +- 标签与应用配置中的版本号一致; - 候选提交已进入 `main`; - 正式标签与候选标签指向同一提交; - DMG、SHA-256、Manifest、版本和双架构一致。 diff --git a/docs/TESTING.md b/docs/TESTING.md index 641b416..9ffb06d 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -1,96 +1,84 @@ # Floatick 测试指南 -Floatick 的自动化测试分为四层。目标不是追求一个模糊的“覆盖率数字”,而是让每一层 +Floatick 的自动化与质量保证分为四层。目标不是追求一个模糊的“覆盖率数字”,而是让每一层 验证它最擅长的边界。 ```mermaid flowchart TB - A["单元与 Repository 测试
领域规则、失败回滚、JSON 持久化"] - B["Widget 测试
组件状态、布局与交互分支"] - C["macOS Integration Test
真实 Flutter 引擎、键盘输入与完整用户链路"] - D["原生与 Release 烟测
AppKit 可访问入口、启动和首次工作区"] + A["Rust 后端单元测试
原子写入、JSON 序列化与文件 I/O 边界"] + B["前端类型与构建检查
TypeScript strict check, React 19 & Tailwind 打包"] + C["IPC 与状态层验证
Zustand 状态流转、TipTap Markdown 解析、标签筛选"] + D["原生与 Release 烟测
macOS Menu Bar 托盘、窗口吸附与极低内存占用"] A --> B --> C --> D ``` -## 一键运行 UI 自动化 +## 本地测试命令 + +### 1. 前端类型检查与生产打包构建 ```bash -tool/test/run_ui_tests.sh +pnpm build ``` -该命令会: - -1. 在真实 macOS Flutter 引擎上运行 `integration_test/floatick_ui_test.dart`; -2. 使用临时目录隔离数据,不会读写 `~/.floatick`; -3. 运行 AppKit 原生可访问入口测试。 - -## 当前自动覆盖 +验证所有 React 组件、TipTap 扩展、Zustand store 与 Tailwind CSS 样式均能通过严谨的 TypeScript 检查并正确打包。 -| 场景 | 覆盖层 | -| --- | --- | -| 首次启动生成欢迎 Todo 和 Tags | Integration + Release smoke | -| 创建含 Markdown 内容的 Todo | Integration | -| 双击详情、完成、归档、恢复、搜索 | Integration | -| 退出前后的本地 JSON 持久化 | Integration | -| Tag 创建、Todo 关联、多选 OR 筛选和清空 | Integration | -| Notes 切换、创建、标签入口与空草稿处理 | Widget | -| Note 搜索、置顶、归档、恢复、共享标签与失败回滚 | Unit + Widget | -| 置顶、登录启动、主题等设置与原生调用边界 | Integration | -| 悬浮图标的 macOS Accessibility button/press contract | XCTest | -| Release 应用启动、独立首次工作区和 JSON 有效性 | Release smoke | - -普通单元与 Widget 测试仍使用: +### 2. Rust 后端单元测试 ```bash -flutter test +cd src-tauri && cargo test ``` -只运行真实 macOS 用户链路: +测试 `commands/storage.rs` 的本地原子持久化、临时文件更名、容错降级及默认数据初始化逻辑。 -```bash -flutter test integration_test/floatick_ui_test.dart -d macos -``` - -只运行原生边界: +### 3. 开发环境全功能联调 ```bash -xcodebuild test \ - -workspace macos/Runner.xcworkspace \ - -scheme Runner \ - -configuration Debug \ - -destination 'platform=macOS' \ - -only-testing:RunnerTests \ - CODE_SIGNING_ALLOWED=NO \ - FLUTTER_TARGET=lib/main.dart +pnpm tauri:dev ``` -## CI - -Pull Request CI 会依次执行: +在本地直接启动 Tauri 调试窗口,验证 macOS 菜单栏状态图标交互、面板自动滑出定位、TipTap 斜杠命令输入与失焦自动隐藏。 -1. 格式与静态检查; -2. 单元和 Widget 测试; -3. macOS UI 自动化与原生边界测试; -4. Universal Release 构建; -5. Release 应用首次启动烟测。 +## 场景验证矩阵 -任何一层失败都会阻止合并。 +| 场景 | 验证层级 | +| --- | --- | +| 首次启动自动初始化 `~/.floatick/` 数据文件与默认标签 | Rust storage test + Smoke test | +| TipTap 编辑器输入 `/` 触发斜杠菜单与 Markdown 解析 | Frontend + Component | +| 待办列表创建、进行中标记、完成、搜索与多标签 OR 筛选 | Zustand Store + UI | +| 笔记列表创建、全屏编辑、置顶、按日期分组与实时保存 | Zustand Store + UI | +| 退出前后的本地 JSON 原子持久化与数据完整性 | Rust Backend + Storage | +| 点击系统菜单栏图标正下方吸附滑出、点击外部失焦自动关闭 | macOS AppKit & Tauri Window | +| 待办未完成数量变动时,实时更新菜单栏图标角标数字 | Rust Tray Manager | +| Release 应用 Universal 架构构建与单进程极低内存占用 | Release smoke test | ## 必须人工验收的系统边界 -Flutter Integration Test 不能操作 macOS 原生系统界面,因此下列行为仍放在 Draft -Release 人工验收中: +部分 macOS 深度集成行为仍放在 Release 人工验收中: -- DMG 拖拽安装、Gatekeeper 和“仍要打开”; -- Sparkle 的真实下载、签名验证、替换应用和重启; -- 多显示器上的悬浮图标拖动与展开方向; -- 60/120Hz 动画、滚动和窗口缩放的主观流畅度; -- 真实登录启动以及不同 macOS 版本的窗口层级。 +- DMG 拖拽安装、首次启动安全校验; +- 菜单栏托盘图标在高分屏(Retina)与多显示器之间的自适应定位; +- 窗口置顶、开机自启系统权限及切换系统明暗外观; +- TipTap 富文本在超长长篇文本下的输入流畅度; +- 窗口打开与收起时的动画平滑度。 -这些项目不是遗漏,而是由操作系统或外部进程控制;自动化负责提前拦截确定性的功能 -回归,Draft 验收负责最终用户环境。 +## 托盘打开闪烁回归 -## 性能与容量 +`src-tauri/src/panel.rs` 的单元测试覆盖失焦关闭请求与重新显示、焦点恢复、托盘按下/ +松开的交错顺序。关闭请求延迟 150 ms 后在主线程复核:请求仍有效、窗口仍可见且 +没有焦点、用户仍开启了点击外部收起,才执行隐藏。新的显示、隐藏、焦点恢复或托盘 +操作会取消旧请求;托盘开关方向在按下时确定。 -Todo 列表的数据基准、10,000 条滚动基准、60/120Hz 指标及本地数据库迁移阈值见 -[Todo 列表性能与容量方案](TODO_LIST_PERFORMANCE.md)。 +这些测试不模拟 AppKit 的原生焦点时序,仍需在 macOS 调试版或候选包验收: + +| 操作 | 预期 | +| --- | --- | +| 从其他应用点击托盘展开,重复多次 | 每次只展开一次,无先显示、消失再显示 | +| 面板展开后再点托盘,包括按住超过 150 ms 再松开 | 只收起一次,不自动重新展开 | +| 连续快速点击托盘 | 每次点击只切换一次,最终状态与点击次数一致 | +| 面板刚展开就点击其他应用 | 失焦稳定约 150 ms 后收起,不因打开保护期而一直停留 | +| 失焦后迅速返回面板,或在关闭请求等待期间重新展开 | 旧请求不会关闭已经恢复焦点或重新展开的面板 | +| 关闭“点击外部收起”后点击其他应用 | 面板保持显示;托盘和 Escape 仍能收起 | +| 在不同显示器的菜单栏展开 | 定位正确,且没有多余的收起/展开 | + +调试版日志中的 `floatick::panel` 会记录托盘按下/松开、开关方向、焦点变化、稳定失焦 +关闭和 `Reopen`,可用于核对偶发问题的事件顺序,不记录待办或笔记内容。 diff --git a/docs/assets/app-icon.png b/docs/assets/app-icon.png new file mode 100644 index 0000000..29ed6f2 Binary files /dev/null and b/docs/assets/app-icon.png differ diff --git a/docs/assets/floatick-product-hero-v6.png b/docs/assets/floatick-product-hero-v6.png index 1c5734a..b50bc3e 100644 Binary files a/docs/assets/floatick-product-hero-v6.png and b/docs/assets/floatick-product-hero-v6.png differ diff --git a/docs/assets/floatick-screenshot-editor.png b/docs/assets/floatick-screenshot-editor.png new file mode 100644 index 0000000..0f44bc3 Binary files /dev/null and b/docs/assets/floatick-screenshot-editor.png differ diff --git a/docs/assets/floatick-screenshot-notes.png b/docs/assets/floatick-screenshot-notes.png new file mode 100644 index 0000000..469336f Binary files /dev/null and b/docs/assets/floatick-screenshot-notes.png differ diff --git a/docs/assets/floatick-screenshot-todos.png b/docs/assets/floatick-screenshot-todos.png new file mode 100644 index 0000000..aa8cc2d Binary files /dev/null and b/docs/assets/floatick-screenshot-todos.png differ diff --git a/docs/assets/floatick-showcase.png b/docs/assets/floatick-showcase.png new file mode 100644 index 0000000..e88dcc0 Binary files /dev/null and b/docs/assets/floatick-showcase.png differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..ac7f7a3 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + Floatick + + +
+ + + diff --git a/.metadata b/legacy/flutter/.metadata similarity index 100% rename from .metadata rename to legacy/flutter/.metadata diff --git a/analysis_options.yaml b/legacy/flutter/analysis_options.yaml similarity index 100% rename from analysis_options.yaml rename to legacy/flutter/analysis_options.yaml diff --git a/benchmark/todo_data_benchmark_test.dart b/legacy/flutter/benchmark/todo_data_benchmark_test.dart similarity index 100% rename from benchmark/todo_data_benchmark_test.dart rename to legacy/flutter/benchmark/todo_data_benchmark_test.dart diff --git a/integration_test/floatick_ui_test.dart b/legacy/flutter/integration_test/floatick_ui_test.dart similarity index 100% rename from integration_test/floatick_ui_test.dart rename to legacy/flutter/integration_test/floatick_ui_test.dart diff --git a/integration_test/todo_scroll_performance_test.dart b/legacy/flutter/integration_test/todo_scroll_performance_test.dart similarity index 100% rename from integration_test/todo_scroll_performance_test.dart rename to legacy/flutter/integration_test/todo_scroll_performance_test.dart diff --git a/l10n.yaml b/legacy/flutter/l10n.yaml similarity index 100% rename from l10n.yaml rename to legacy/flutter/l10n.yaml diff --git a/lib/app/floatick_app.dart b/legacy/flutter/lib/app/floatick_app.dart similarity index 100% rename from lib/app/floatick_app.dart rename to legacy/flutter/lib/app/floatick_app.dart diff --git a/lib/app/theme/floatick_theme.dart b/legacy/flutter/lib/app/theme/floatick_theme.dart similarity index 100% rename from lib/app/theme/floatick_theme.dart rename to legacy/flutter/lib/app/theme/floatick_theme.dart diff --git a/lib/core/platform/window_bridge.dart b/legacy/flutter/lib/core/platform/window_bridge.dart similarity index 100% rename from lib/core/platform/window_bridge.dart rename to legacy/flutter/lib/core/platform/window_bridge.dart diff --git a/lib/core/storage/storage_failure.dart b/legacy/flutter/lib/core/storage/storage_failure.dart similarity index 100% rename from lib/core/storage/storage_failure.dart rename to legacy/flutter/lib/core/storage/storage_failure.dart diff --git a/lib/core/ui/floatick_brand_mark.dart b/legacy/flutter/lib/core/ui/floatick_brand_mark.dart similarity index 100% rename from lib/core/ui/floatick_brand_mark.dart rename to legacy/flutter/lib/core/ui/floatick_brand_mark.dart diff --git a/lib/core/ui/floatick_editor_components.dart b/legacy/flutter/lib/core/ui/floatick_editor_components.dart similarity index 100% rename from lib/core/ui/floatick_editor_components.dart rename to legacy/flutter/lib/core/ui/floatick_editor_components.dart diff --git a/lib/core/ui/floatick_hover_motion.dart b/legacy/flutter/lib/core/ui/floatick_hover_motion.dart similarity index 100% rename from lib/core/ui/floatick_hover_motion.dart rename to legacy/flutter/lib/core/ui/floatick_hover_motion.dart diff --git a/lib/core/ui/floatick_markdown.dart b/legacy/flutter/lib/core/ui/floatick_markdown.dart similarity index 100% rename from lib/core/ui/floatick_markdown.dart rename to legacy/flutter/lib/core/ui/floatick_markdown.dart diff --git a/lib/core/ui/floatick_modal_bottom_sheet.dart b/legacy/flutter/lib/core/ui/floatick_modal_bottom_sheet.dart similarity index 100% rename from lib/core/ui/floatick_modal_bottom_sheet.dart rename to legacy/flutter/lib/core/ui/floatick_modal_bottom_sheet.dart diff --git a/lib/core/ui/floatick_surface_metrics.dart b/legacy/flutter/lib/core/ui/floatick_surface_metrics.dart similarity index 100% rename from lib/core/ui/floatick_surface_metrics.dart rename to legacy/flutter/lib/core/ui/floatick_surface_metrics.dart diff --git a/lib/features/notes/data/note_repository.dart b/legacy/flutter/lib/features/notes/data/note_repository.dart similarity index 100% rename from lib/features/notes/data/note_repository.dart rename to legacy/flutter/lib/features/notes/data/note_repository.dart diff --git a/lib/features/notes/domain/note_item.dart b/legacy/flutter/lib/features/notes/domain/note_item.dart similarity index 100% rename from lib/features/notes/domain/note_item.dart rename to legacy/flutter/lib/features/notes/domain/note_item.dart diff --git a/lib/features/notes/presentation/note_editor_drawer.dart b/legacy/flutter/lib/features/notes/presentation/note_editor_drawer.dart similarity index 100% rename from lib/features/notes/presentation/note_editor_drawer.dart rename to legacy/flutter/lib/features/notes/presentation/note_editor_drawer.dart diff --git a/lib/features/notes/presentation/note_panel_content.dart b/legacy/flutter/lib/features/notes/presentation/note_panel_content.dart similarity index 100% rename from lib/features/notes/presentation/note_panel_content.dart rename to legacy/flutter/lib/features/notes/presentation/note_panel_content.dart diff --git a/lib/features/notes/presentation/note_view_model.dart b/legacy/flutter/lib/features/notes/presentation/note_view_model.dart similarity index 100% rename from lib/features/notes/presentation/note_view_model.dart rename to legacy/flutter/lib/features/notes/presentation/note_view_model.dart diff --git a/lib/features/settings/data/login_item_repository.dart b/legacy/flutter/lib/features/settings/data/login_item_repository.dart similarity index 100% rename from lib/features/settings/data/login_item_repository.dart rename to legacy/flutter/lib/features/settings/data/login_item_repository.dart diff --git a/lib/features/settings/data/settings_repository.dart b/legacy/flutter/lib/features/settings/data/settings_repository.dart similarity index 100% rename from lib/features/settings/data/settings_repository.dart rename to legacy/flutter/lib/features/settings/data/settings_repository.dart diff --git a/lib/features/settings/domain/app_settings.dart b/legacy/flutter/lib/features/settings/domain/app_settings.dart similarity index 100% rename from lib/features/settings/domain/app_settings.dart rename to legacy/flutter/lib/features/settings/domain/app_settings.dart diff --git a/lib/features/settings/domain/login_item_status.dart b/legacy/flutter/lib/features/settings/domain/login_item_status.dart similarity index 100% rename from lib/features/settings/domain/login_item_status.dart rename to legacy/flutter/lib/features/settings/domain/login_item_status.dart diff --git a/lib/features/settings/presentation/settings_drawer.dart b/legacy/flutter/lib/features/settings/presentation/settings_drawer.dart similarity index 100% rename from lib/features/settings/presentation/settings_drawer.dart rename to legacy/flutter/lib/features/settings/presentation/settings_drawer.dart diff --git a/lib/features/settings/presentation/settings_view_model.dart b/legacy/flutter/lib/features/settings/presentation/settings_view_model.dart similarity index 100% rename from lib/features/settings/presentation/settings_view_model.dart rename to legacy/flutter/lib/features/settings/presentation/settings_view_model.dart diff --git a/lib/features/settings/presentation/widgets/compact_settings_item.dart b/legacy/flutter/lib/features/settings/presentation/widgets/compact_settings_item.dart similarity index 100% rename from lib/features/settings/presentation/widgets/compact_settings_item.dart rename to legacy/flutter/lib/features/settings/presentation/widgets/compact_settings_item.dart diff --git a/lib/features/settings/presentation/widgets/compact_settings_toggle.dart b/legacy/flutter/lib/features/settings/presentation/widgets/compact_settings_toggle.dart similarity index 100% rename from lib/features/settings/presentation/widgets/compact_settings_toggle.dart rename to legacy/flutter/lib/features/settings/presentation/widgets/compact_settings_toggle.dart diff --git a/lib/features/settings/presentation/widgets/update_settings_section.dart b/legacy/flutter/lib/features/settings/presentation/widgets/update_settings_section.dart similarity index 100% rename from lib/features/settings/presentation/widgets/update_settings_section.dart rename to legacy/flutter/lib/features/settings/presentation/widgets/update_settings_section.dart diff --git a/lib/features/todos/data/first_run_workspace_seeder.dart b/legacy/flutter/lib/features/todos/data/first_run_workspace_seeder.dart similarity index 100% rename from lib/features/todos/data/first_run_workspace_seeder.dart rename to legacy/flutter/lib/features/todos/data/first_run_workspace_seeder.dart diff --git a/lib/features/todos/data/tag_repository.dart b/legacy/flutter/lib/features/todos/data/tag_repository.dart similarity index 100% rename from lib/features/todos/data/tag_repository.dart rename to legacy/flutter/lib/features/todos/data/tag_repository.dart diff --git a/lib/features/todos/data/todo_repository.dart b/legacy/flutter/lib/features/todos/data/todo_repository.dart similarity index 100% rename from lib/features/todos/data/todo_repository.dart rename to legacy/flutter/lib/features/todos/data/todo_repository.dart diff --git a/lib/features/todos/domain/tag_workspace.dart b/legacy/flutter/lib/features/todos/domain/tag_workspace.dart similarity index 100% rename from lib/features/todos/domain/tag_workspace.dart rename to legacy/flutter/lib/features/todos/domain/tag_workspace.dart diff --git a/lib/features/todos/domain/todo_item.dart b/legacy/flutter/lib/features/todos/domain/todo_item.dart similarity index 100% rename from lib/features/todos/domain/todo_item.dart rename to legacy/flutter/lib/features/todos/domain/todo_item.dart diff --git a/lib/features/todos/domain/todo_markdown_formatter.dart b/legacy/flutter/lib/features/todos/domain/todo_markdown_formatter.dart similarity index 100% rename from lib/features/todos/domain/todo_markdown_formatter.dart rename to legacy/flutter/lib/features/todos/domain/todo_markdown_formatter.dart diff --git a/lib/features/todos/domain/todo_tag.dart b/legacy/flutter/lib/features/todos/domain/todo_tag.dart similarity index 100% rename from lib/features/todos/domain/todo_tag.dart rename to legacy/flutter/lib/features/todos/domain/todo_tag.dart diff --git a/lib/features/todos/presentation/deadline_reminder_scheduler.dart b/legacy/flutter/lib/features/todos/presentation/deadline_reminder_scheduler.dart similarity index 100% rename from lib/features/todos/presentation/deadline_reminder_scheduler.dart rename to legacy/flutter/lib/features/todos/presentation/deadline_reminder_scheduler.dart diff --git a/lib/features/todos/presentation/tag_filter_drawer.dart b/legacy/flutter/lib/features/todos/presentation/tag_filter_drawer.dart similarity index 100% rename from lib/features/todos/presentation/tag_filter_drawer.dart rename to legacy/flutter/lib/features/todos/presentation/tag_filter_drawer.dart diff --git a/lib/features/todos/presentation/tag_management_drawer.dart b/legacy/flutter/lib/features/todos/presentation/tag_management_drawer.dart similarity index 100% rename from lib/features/todos/presentation/tag_management_drawer.dart rename to legacy/flutter/lib/features/todos/presentation/tag_management_drawer.dart diff --git a/lib/features/todos/presentation/todo_clipboard_controller.dart b/legacy/flutter/lib/features/todos/presentation/todo_clipboard_controller.dart similarity index 100% rename from lib/features/todos/presentation/todo_clipboard_controller.dart rename to legacy/flutter/lib/features/todos/presentation/todo_clipboard_controller.dart diff --git a/lib/features/todos/presentation/todo_deadline_picker.dart b/legacy/flutter/lib/features/todos/presentation/todo_deadline_picker.dart similarity index 100% rename from lib/features/todos/presentation/todo_deadline_picker.dart rename to legacy/flutter/lib/features/todos/presentation/todo_deadline_picker.dart diff --git a/lib/features/todos/presentation/todo_editor_drawer.dart b/legacy/flutter/lib/features/todos/presentation/todo_editor_drawer.dart similarity index 100% rename from lib/features/todos/presentation/todo_editor_drawer.dart rename to legacy/flutter/lib/features/todos/presentation/todo_editor_drawer.dart diff --git a/lib/features/todos/presentation/todo_panel.dart b/legacy/flutter/lib/features/todos/presentation/todo_panel.dart similarity index 100% rename from lib/features/todos/presentation/todo_panel.dart rename to legacy/flutter/lib/features/todos/presentation/todo_panel.dart diff --git a/lib/features/todos/presentation/todo_view_model.dart b/legacy/flutter/lib/features/todos/presentation/todo_view_model.dart similarity index 100% rename from lib/features/todos/presentation/todo_view_model.dart rename to legacy/flutter/lib/features/todos/presentation/todo_view_model.dart diff --git a/lib/features/todos/presentation/widgets/editor_tag_selector.dart b/legacy/flutter/lib/features/todos/presentation/widgets/editor_tag_selector.dart similarity index 100% rename from lib/features/todos/presentation/widgets/editor_tag_selector.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/editor_tag_selector.dart diff --git a/lib/features/todos/presentation/widgets/floatick_tag_chip.dart b/legacy/flutter/lib/features/todos/presentation/widgets/floatick_tag_chip.dart similarity index 100% rename from lib/features/todos/presentation/widgets/floatick_tag_chip.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/floatick_tag_chip.dart diff --git a/lib/features/todos/presentation/widgets/floating_todo_icon.dart b/legacy/flutter/lib/features/todos/presentation/widgets/floating_todo_icon.dart similarity index 100% rename from lib/features/todos/presentation/widgets/floating_todo_icon.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/floating_todo_icon.dart diff --git a/lib/features/todos/presentation/widgets/tag_menus.dart b/legacy/flutter/lib/features/todos/presentation/widgets/tag_menus.dart similarity index 100% rename from lib/features/todos/presentation/widgets/tag_menus.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/tag_menus.dart diff --git a/lib/features/todos/presentation/widgets/tag_palette.dart b/legacy/flutter/lib/features/todos/presentation/widgets/tag_palette.dart similarity index 100% rename from lib/features/todos/presentation/widgets/tag_palette.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/tag_palette.dart diff --git a/lib/features/todos/presentation/widgets/tag_selection_row.dart b/legacy/flutter/lib/features/todos/presentation/widgets/tag_selection_row.dart similarity index 100% rename from lib/features/todos/presentation/widgets/tag_selection_row.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/tag_selection_row.dart diff --git a/lib/features/todos/presentation/widgets/todo_actions_bottom_sheet.dart b/legacy/flutter/lib/features/todos/presentation/widgets/todo_actions_bottom_sheet.dart similarity index 100% rename from lib/features/todos/presentation/widgets/todo_actions_bottom_sheet.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/todo_actions_bottom_sheet.dart diff --git a/lib/features/todos/presentation/widgets/todo_copy_button.dart b/legacy/flutter/lib/features/todos/presentation/widgets/todo_copy_button.dart similarity index 100% rename from lib/features/todos/presentation/widgets/todo_copy_button.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/todo_copy_button.dart diff --git a/lib/features/todos/presentation/widgets/todo_list_row.dart b/legacy/flutter/lib/features/todos/presentation/widgets/todo_list_row.dart similarity index 100% rename from lib/features/todos/presentation/widgets/todo_list_row.dart rename to legacy/flutter/lib/features/todos/presentation/widgets/todo_list_row.dart diff --git a/lib/features/updates/data/update_repository.dart b/legacy/flutter/lib/features/updates/data/update_repository.dart similarity index 100% rename from lib/features/updates/data/update_repository.dart rename to legacy/flutter/lib/features/updates/data/update_repository.dart diff --git a/lib/features/updates/domain/update_settings_snapshot.dart b/legacy/flutter/lib/features/updates/domain/update_settings_snapshot.dart similarity index 100% rename from lib/features/updates/domain/update_settings_snapshot.dart rename to legacy/flutter/lib/features/updates/domain/update_settings_snapshot.dart diff --git a/lib/features/updates/presentation/update_view_model.dart b/legacy/flutter/lib/features/updates/presentation/update_view_model.dart similarity index 100% rename from lib/features/updates/presentation/update_view_model.dart rename to legacy/flutter/lib/features/updates/presentation/update_view_model.dart diff --git a/lib/l10n/app_en.arb b/legacy/flutter/lib/l10n/app_en.arb similarity index 100% rename from lib/l10n/app_en.arb rename to legacy/flutter/lib/l10n/app_en.arb diff --git a/lib/l10n/app_localizations.dart b/legacy/flutter/lib/l10n/app_localizations.dart similarity index 100% rename from lib/l10n/app_localizations.dart rename to legacy/flutter/lib/l10n/app_localizations.dart diff --git a/lib/l10n/app_localizations_en.dart b/legacy/flutter/lib/l10n/app_localizations_en.dart similarity index 100% rename from lib/l10n/app_localizations_en.dart rename to legacy/flutter/lib/l10n/app_localizations_en.dart diff --git a/lib/l10n/app_localizations_zh.dart b/legacy/flutter/lib/l10n/app_localizations_zh.dart similarity index 100% rename from lib/l10n/app_localizations_zh.dart rename to legacy/flutter/lib/l10n/app_localizations_zh.dart diff --git a/lib/l10n/app_zh.arb b/legacy/flutter/lib/l10n/app_zh.arb similarity index 100% rename from lib/l10n/app_zh.arb rename to legacy/flutter/lib/l10n/app_zh.arb diff --git a/lib/l10n/l10n.dart b/legacy/flutter/lib/l10n/l10n.dart similarity index 100% rename from lib/l10n/l10n.dart rename to legacy/flutter/lib/l10n/l10n.dart diff --git a/lib/l10n/storage_failure_localizations.dart b/legacy/flutter/lib/l10n/storage_failure_localizations.dart similarity index 100% rename from lib/l10n/storage_failure_localizations.dart rename to legacy/flutter/lib/l10n/storage_failure_localizations.dart diff --git a/lib/main.dart b/legacy/flutter/lib/main.dart similarity index 100% rename from lib/main.dart rename to legacy/flutter/lib/main.dart diff --git a/macos/.gitignore b/legacy/flutter/macos/.gitignore similarity index 100% rename from macos/.gitignore rename to legacy/flutter/macos/.gitignore diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/legacy/flutter/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from macos/Flutter/Flutter-Debug.xcconfig rename to legacy/flutter/macos/Flutter/Flutter-Debug.xcconfig diff --git a/macos/Flutter/Flutter-Release.xcconfig b/legacy/flutter/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from macos/Flutter/Flutter-Release.xcconfig rename to legacy/flutter/macos/Flutter/Flutter-Release.xcconfig diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/legacy/flutter/macos/Flutter/GeneratedPluginRegistrant.swift similarity index 100% rename from macos/Flutter/GeneratedPluginRegistrant.swift rename to legacy/flutter/macos/Flutter/GeneratedPluginRegistrant.swift diff --git a/macos/Runner.xcodeproj/project.pbxproj b/legacy/flutter/macos/Runner.xcodeproj/project.pbxproj similarity index 100% rename from macos/Runner.xcodeproj/project.pbxproj rename to legacy/flutter/macos/Runner.xcodeproj/project.pbxproj diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/legacy/flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to legacy/flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/legacy/flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to legacy/flutter/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/legacy/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to legacy/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/legacy/flutter/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from macos/Runner.xcworkspace/contents.xcworkspacedata rename to legacy/flutter/macos/Runner.xcworkspace/contents.xcworkspacedata diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/legacy/flutter/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to legacy/flutter/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/legacy/flutter/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/legacy/flutter/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..d4c2188 --- /dev/null +++ b/legacy/flutter/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "sparkle", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sparkle-project/Sparkle", + "state" : { + "revision" : "6276ba2b404829d139c45ff98427cf90e2efc59b", + "version" : "2.9.2" + } + } + ], + "version" : 2 +} diff --git a/macos/Runner/AppDelegate.swift b/legacy/flutter/macos/Runner/AppDelegate.swift similarity index 100% rename from macos/Runner/AppDelegate.swift rename to legacy/flutter/macos/Runner/AppDelegate.swift diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to legacy/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/legacy/flutter/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from macos/Runner/Base.lproj/MainMenu.xib rename to legacy/flutter/macos/Runner/Base.lproj/MainMenu.xib diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/legacy/flutter/macos/Runner/Configs/AppInfo.xcconfig similarity index 100% rename from macos/Runner/Configs/AppInfo.xcconfig rename to legacy/flutter/macos/Runner/Configs/AppInfo.xcconfig diff --git a/macos/Runner/Configs/Debug.xcconfig b/legacy/flutter/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from macos/Runner/Configs/Debug.xcconfig rename to legacy/flutter/macos/Runner/Configs/Debug.xcconfig diff --git a/macos/Runner/Configs/Release.xcconfig b/legacy/flutter/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from macos/Runner/Configs/Release.xcconfig rename to legacy/flutter/macos/Runner/Configs/Release.xcconfig diff --git a/macos/Runner/Configs/Warnings.xcconfig b/legacy/flutter/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from macos/Runner/Configs/Warnings.xcconfig rename to legacy/flutter/macos/Runner/Configs/Warnings.xcconfig diff --git a/macos/Runner/DebugProfile.entitlements b/legacy/flutter/macos/Runner/DebugProfile.entitlements similarity index 100% rename from macos/Runner/DebugProfile.entitlements rename to legacy/flutter/macos/Runner/DebugProfile.entitlements diff --git a/macos/Runner/Info.plist b/legacy/flutter/macos/Runner/Info.plist similarity index 100% rename from macos/Runner/Info.plist rename to legacy/flutter/macos/Runner/Info.plist diff --git a/macos/Runner/LoginItemService.swift b/legacy/flutter/macos/Runner/LoginItemService.swift similarity index 100% rename from macos/Runner/LoginItemService.swift rename to legacy/flutter/macos/Runner/LoginItemService.swift diff --git a/macos/Runner/MainFlutterWindow.swift b/legacy/flutter/macos/Runner/MainFlutterWindow.swift similarity index 100% rename from macos/Runner/MainFlutterWindow.swift rename to legacy/flutter/macos/Runner/MainFlutterWindow.swift diff --git a/macos/Runner/Release.entitlements b/legacy/flutter/macos/Runner/Release.entitlements similarity index 100% rename from macos/Runner/Release.entitlements rename to legacy/flutter/macos/Runner/Release.entitlements diff --git a/macos/Runner/UpdateService.swift b/legacy/flutter/macos/Runner/UpdateService.swift similarity index 100% rename from macos/Runner/UpdateService.swift rename to legacy/flutter/macos/Runner/UpdateService.swift diff --git a/macos/RunnerTests/RunnerTests.swift b/legacy/flutter/macos/RunnerTests/RunnerTests.swift similarity index 100% rename from macos/RunnerTests/RunnerTests.swift rename to legacy/flutter/macos/RunnerTests/RunnerTests.swift diff --git a/pubspec.lock b/legacy/flutter/pubspec.lock similarity index 100% rename from pubspec.lock rename to legacy/flutter/pubspec.lock diff --git a/pubspec.yaml b/legacy/flutter/pubspec.yaml similarity index 100% rename from pubspec.yaml rename to legacy/flutter/pubspec.yaml diff --git a/test/app/floatick_app_test.dart b/legacy/flutter/test/app/floatick_app_test.dart similarity index 100% rename from test/app/floatick_app_test.dart rename to legacy/flutter/test/app/floatick_app_test.dart diff --git a/test/app/theme/floatick_theme_test.dart b/legacy/flutter/test/app/theme/floatick_theme_test.dart similarity index 100% rename from test/app/theme/floatick_theme_test.dart rename to legacy/flutter/test/app/theme/floatick_theme_test.dart diff --git a/test/core/platform/window_bridge_test.dart b/legacy/flutter/test/core/platform/window_bridge_test.dart similarity index 100% rename from test/core/platform/window_bridge_test.dart rename to legacy/flutter/test/core/platform/window_bridge_test.dart diff --git a/test/core/ui/floatick_editor_components_test.dart b/legacy/flutter/test/core/ui/floatick_editor_components_test.dart similarity index 100% rename from test/core/ui/floatick_editor_components_test.dart rename to legacy/flutter/test/core/ui/floatick_editor_components_test.dart diff --git a/test/core/ui/floatick_hover_motion_test.dart b/legacy/flutter/test/core/ui/floatick_hover_motion_test.dart similarity index 100% rename from test/core/ui/floatick_hover_motion_test.dart rename to legacy/flutter/test/core/ui/floatick_hover_motion_test.dart diff --git a/test/core/ui/floatick_markdown_test.dart b/legacy/flutter/test/core/ui/floatick_markdown_test.dart similarity index 100% rename from test/core/ui/floatick_markdown_test.dart rename to legacy/flutter/test/core/ui/floatick_markdown_test.dart diff --git a/test/features/notes/data/note_repository_test.dart b/legacy/flutter/test/features/notes/data/note_repository_test.dart similarity index 100% rename from test/features/notes/data/note_repository_test.dart rename to legacy/flutter/test/features/notes/data/note_repository_test.dart diff --git a/test/features/notes/presentation/note_editor_drawer_test.dart b/legacy/flutter/test/features/notes/presentation/note_editor_drawer_test.dart similarity index 100% rename from test/features/notes/presentation/note_editor_drawer_test.dart rename to legacy/flutter/test/features/notes/presentation/note_editor_drawer_test.dart diff --git a/test/features/notes/presentation/note_panel_content_test.dart b/legacy/flutter/test/features/notes/presentation/note_panel_content_test.dart similarity index 100% rename from test/features/notes/presentation/note_panel_content_test.dart rename to legacy/flutter/test/features/notes/presentation/note_panel_content_test.dart diff --git a/test/features/notes/presentation/note_view_model_test.dart b/legacy/flutter/test/features/notes/presentation/note_view_model_test.dart similarity index 100% rename from test/features/notes/presentation/note_view_model_test.dart rename to legacy/flutter/test/features/notes/presentation/note_view_model_test.dart diff --git a/test/features/settings/data/login_item_repository_test.dart b/legacy/flutter/test/features/settings/data/login_item_repository_test.dart similarity index 100% rename from test/features/settings/data/login_item_repository_test.dart rename to legacy/flutter/test/features/settings/data/login_item_repository_test.dart diff --git a/test/features/settings/data/settings_repository_test.dart b/legacy/flutter/test/features/settings/data/settings_repository_test.dart similarity index 100% rename from test/features/settings/data/settings_repository_test.dart rename to legacy/flutter/test/features/settings/data/settings_repository_test.dart diff --git a/test/features/settings/presentation/settings_view_model_test.dart b/legacy/flutter/test/features/settings/presentation/settings_view_model_test.dart similarity index 100% rename from test/features/settings/presentation/settings_view_model_test.dart rename to legacy/flutter/test/features/settings/presentation/settings_view_model_test.dart diff --git a/test/features/todos/data/first_run_workspace_seeder_test.dart b/legacy/flutter/test/features/todos/data/first_run_workspace_seeder_test.dart similarity index 100% rename from test/features/todos/data/first_run_workspace_seeder_test.dart rename to legacy/flutter/test/features/todos/data/first_run_workspace_seeder_test.dart diff --git a/test/features/todos/data/tag_repository_test.dart b/legacy/flutter/test/features/todos/data/tag_repository_test.dart similarity index 100% rename from test/features/todos/data/tag_repository_test.dart rename to legacy/flutter/test/features/todos/data/tag_repository_test.dart diff --git a/test/features/todos/data/todo_repository_test.dart b/legacy/flutter/test/features/todos/data/todo_repository_test.dart similarity index 100% rename from test/features/todos/data/todo_repository_test.dart rename to legacy/flutter/test/features/todos/data/todo_repository_test.dart diff --git a/test/features/todos/domain/todo_markdown_formatter_test.dart b/legacy/flutter/test/features/todos/domain/todo_markdown_formatter_test.dart similarity index 100% rename from test/features/todos/domain/todo_markdown_formatter_test.dart rename to legacy/flutter/test/features/todos/domain/todo_markdown_formatter_test.dart diff --git a/test/features/todos/presentation/deadline_reminder_scheduler_test.dart b/legacy/flutter/test/features/todos/presentation/deadline_reminder_scheduler_test.dart similarity index 100% rename from test/features/todos/presentation/deadline_reminder_scheduler_test.dart rename to legacy/flutter/test/features/todos/presentation/deadline_reminder_scheduler_test.dart diff --git a/test/features/todos/presentation/todo_editor_drawer_test.dart b/legacy/flutter/test/features/todos/presentation/todo_editor_drawer_test.dart similarity index 100% rename from test/features/todos/presentation/todo_editor_drawer_test.dart rename to legacy/flutter/test/features/todos/presentation/todo_editor_drawer_test.dart diff --git a/test/features/todos/presentation/todo_list_row_test.dart b/legacy/flutter/test/features/todos/presentation/todo_list_row_test.dart similarity index 100% rename from test/features/todos/presentation/todo_list_row_test.dart rename to legacy/flutter/test/features/todos/presentation/todo_list_row_test.dart diff --git a/test/features/todos/presentation/todo_view_model_test.dart b/legacy/flutter/test/features/todos/presentation/todo_view_model_test.dart similarity index 100% rename from test/features/todos/presentation/todo_view_model_test.dart rename to legacy/flutter/test/features/todos/presentation/todo_view_model_test.dart diff --git a/test/features/updates/data/update_repository_test.dart b/legacy/flutter/test/features/updates/data/update_repository_test.dart similarity index 100% rename from test/features/updates/data/update_repository_test.dart rename to legacy/flutter/test/features/updates/data/update_repository_test.dart diff --git a/test/features/updates/domain/update_settings_snapshot_test.dart b/legacy/flutter/test/features/updates/domain/update_settings_snapshot_test.dart similarity index 100% rename from test/features/updates/domain/update_settings_snapshot_test.dart rename to legacy/flutter/test/features/updates/domain/update_settings_snapshot_test.dart diff --git a/test/features/updates/presentation/update_view_model_test.dart b/legacy/flutter/test/features/updates/presentation/update_view_model_test.dart similarity index 100% rename from test/features/updates/presentation/update_view_model_test.dart rename to legacy/flutter/test/features/updates/presentation/update_view_model_test.dart diff --git a/tool/floatick_app_icon.svg b/legacy/flutter/tool/floatick_app_icon.svg similarity index 100% rename from tool/floatick_app_icon.svg rename to legacy/flutter/tool/floatick_app_icon.svg diff --git a/tool/generate_macos_icon.swift b/legacy/flutter/tool/generate_macos_icon.swift similarity index 100% rename from tool/generate_macos_icon.swift rename to legacy/flutter/tool/generate_macos_icon.swift diff --git a/tool/release/ExportOptions.plist b/legacy/flutter/tool/release/ExportOptions.plist similarity index 100% rename from tool/release/ExportOptions.plist rename to legacy/flutter/tool/release/ExportOptions.plist diff --git a/tool/release/create_dmg.sh b/legacy/flutter/tool/release/create_dmg.sh similarity index 100% rename from tool/release/create_dmg.sh rename to legacy/flutter/tool/release/create_dmg.sh diff --git a/tool/release/prepare_unsigned_app.sh b/legacy/flutter/tool/release/prepare_unsigned_app.sh similarity index 100% rename from tool/release/prepare_unsigned_app.sh rename to legacy/flutter/tool/release/prepare_unsigned_app.sh diff --git a/tool/release/smoke_test_app.sh b/legacy/flutter/tool/release/smoke_test_app.sh similarity index 100% rename from tool/release/smoke_test_app.sh rename to legacy/flutter/tool/release/smoke_test_app.sh diff --git a/tool/release/unsigned_release.entitlements b/legacy/flutter/tool/release/unsigned_release.entitlements similarity index 100% rename from tool/release/unsigned_release.entitlements rename to legacy/flutter/tool/release/unsigned_release.entitlements diff --git a/tool/release/verify_update_feed.sh b/legacy/flutter/tool/release/verify_update_feed.sh similarity index 100% rename from tool/release/verify_update_feed.sh rename to legacy/flutter/tool/release/verify_update_feed.sh diff --git a/tool/test/run_ui_tests.sh b/legacy/flutter/tool/test/run_ui_tests.sh similarity index 100% rename from tool/test/run_ui_tests.sh rename to legacy/flutter/tool/test/run_ui_tests.sh diff --git a/package.json b/package.json new file mode 100644 index 0000000..30ef658 --- /dev/null +++ b/package.json @@ -0,0 +1,58 @@ +{ + "name": "floatick", + "version": "0.4.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "tauri": "tauri", + "tauri:dev": "tauri dev", + "tauri:build": "tauri build" + }, + "packageManager": "pnpm@11.7.0", + "dependencies": { + "@fontsource/fira-code": "^5.3.0", + "@fontsource/google-sans": "^5.3.1", + "@phosphor-icons/react": "^2.1.10", + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/plugin-autostart": "^2.5.1", + "@tauri-apps/plugin-opener": "^2.5.5", + "@tiptap/core": "^3.31.0", + "@tiptap/extension-placeholder": "^3.31.0", + "@tiptap/extension-task-item": "^3.31.0", + "@tiptap/extension-task-list": "^3.31.0", + "@tiptap/pm": "^3.31.0", + "@tiptap/react": "^3.31.0", + "@tiptap/starter-kit": "^3.31.0", + "@types/markdown-it": "^14.2.0", + "clsx": "^2.1.1", + "date-fns": "^4.4.0", + "i18next": "^26.4.1", + "markdown-it": "^15.0.1", + "markdown-it-task-lists": "^2.1.1", + "prosemirror-markdown": "^1.13.7", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-i18next": "^17.0.13", + "react-markdown": "^10.1.0", + "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.6.0", + "tiptap-markdown": "^0.9.0", + "zustand": "^5.0.15" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.3.3", + "@tauri-apps/cli": "^2.11.4", + "@types/node": "^26.4.1", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.5", + "@vitejs/plugin-react": "^6.1.1", + "autoprefixer": "^10.5.4", + "postcss": "^8.5.26", + "tailwindcss": "^4.3.3", + "typescript": "^7.0.2", + "vite": "^8.2.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..bfcba02 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3095 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@fontsource/fira-code': + specifier: ^5.3.0 + version: 5.3.0 + '@fontsource/google-sans': + specifier: ^5.3.1 + version: 5.3.1 + '@phosphor-icons/react': + specifier: ^2.1.10 + version: 2.1.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tauri-apps/api': + specifier: ^2.11.1 + version: 2.11.1 + '@tauri-apps/plugin-autostart': + specifier: ^2.5.1 + version: 2.5.1 + '@tauri-apps/plugin-opener': + specifier: ^2.5.5 + version: 2.5.5 + '@tiptap/core': + specifier: ^3.31.0 + version: 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/extension-placeholder': + specifier: ^3.31.0 + version: 3.31.0(@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-task-item': + specifier: ^3.31.0 + version: 3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-task-list': + specifier: ^3.31.0 + version: 3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/pm': + specifier: ^3.31.0 + version: 3.31.0 + '@tiptap/react': + specifier: ^3.31.0 + version: 3.31.0(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tiptap/starter-kit': + specifier: ^3.31.0 + version: 3.31.0 + '@types/markdown-it': + specifier: ^14.2.0 + version: 14.2.0 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + date-fns: + specifier: ^4.4.0 + version: 4.4.0 + i18next: + specifier: ^26.4.1 + version: 26.4.1(typescript@7.0.2) + markdown-it: + specifier: ^15.0.1 + version: 15.0.1 + markdown-it-task-lists: + specifier: ^2.1.1 + version: 2.1.1 + prosemirror-markdown: + specifier: ^1.13.7 + version: 1.13.7 + react: + specifier: ^19.2.8 + version: 19.2.8 + react-dom: + specifier: ^19.2.8 + version: 19.2.8(react@19.2.8) + react-i18next: + specifier: ^17.0.13 + version: 17.0.13(i18next@26.4.1(typescript@7.0.2))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.18)(react@19.2.8) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + tiptap-markdown: + specifier: ^0.9.0 + version: 0.9.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + zustand: + specifier: ^5.0.15 + version: 5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + devDependencies: + '@tailwindcss/vite': + specifier: ^4.3.3 + version: 4.3.3(vite@8.2.2(@types/node@26.4.1)(jiti@2.7.0)) + '@tauri-apps/cli': + specifier: ^2.11.4 + version: 2.11.4 + '@types/node': + specifier: ^26.4.1 + version: 26.4.1 + '@types/react': + specifier: ^19.2.18 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.5 + version: 19.2.5(@types/react@19.2.18) + '@vitejs/plugin-react': + specifier: ^6.1.1 + version: 6.1.1(vite@8.2.2(@types/node@26.4.1)(jiti@2.7.0)) + autoprefixer: + specifier: ^10.5.4 + version: 10.5.4(postcss@8.5.26) + postcss: + specifier: ^8.5.26 + version: 8.5.26 + tailwindcss: + specifier: ^4.3.3 + version: 4.3.3 + typescript: + specifier: ^7.0.2 + version: 7.0.2 + vite: + specifier: ^8.2.2 + version: 8.2.2(@types/node@26.4.1)(jiti@2.7.0) + +packages: + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@fontsource/fira-code@5.3.0': + resolution: {integrity: sha512-EJL968RJRkakubAj/coU8pSUaeTE5UNoRjtzAr6kGiSZ3jWuN8/AKWHwym/PFUaQL1q7IL/H+EXs4358YhrTBQ==} + + '@fontsource/google-sans@5.3.1': + resolution: {integrity: sha512-vPaxyKtWMAecphz5CHerhBAIhnRNzQs0dn4KEr8d9Zx5MsoRBXZgFG43Lso2h7w46HVb7S+ku1sdVkGvONz1kQ==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@oxc-project/types@0.147.0': + resolution: {integrity: sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==} + + '@phosphor-icons/react@2.1.10': + resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} + engines: {node: '>=10'} + peerDependencies: + react: '>= 16.8' + react-dom: '>= 16.8' + + '@rolldown/binding-android-arm-eabi@1.2.6': + resolution: {integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.6': + resolution: {integrity: sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.6': + resolution: {integrity: sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.6': + resolution: {integrity: sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.6': + resolution: {integrity: sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.6': + resolution: {integrity: sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.6': + resolution: {integrity: sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.6': + resolution: {integrity: sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.6': + resolution: {integrity: sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.6': + resolution: {integrity: sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.6': + resolution: {integrity: sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.6': + resolution: {integrity: sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.6': + resolution: {integrity: sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.6': + resolution: {integrity: sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.6': + resolution: {integrity: sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.3': + resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.3': + resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tauri-apps/api@2.11.1': + resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} + + '@tauri-apps/cli-darwin-arm64@2.11.4': + resolution: {integrity: sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.11.4': + resolution: {integrity: sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': + resolution: {integrity: sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.11.4': + resolution: {integrity: sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-arm64-musl@2.11.4': + resolution: {integrity: sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': + resolution: {integrity: sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-x64-gnu@2.11.4': + resolution: {integrity: sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tauri-apps/cli-linux-x64-musl@2.11.4': + resolution: {integrity: sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tauri-apps/cli-win32-arm64-msvc@2.11.4': + resolution: {integrity: sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tauri-apps/cli-win32-ia32-msvc@2.11.4': + resolution: {integrity: sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@tauri-apps/cli-win32-x64-msvc@2.11.4': + resolution: {integrity: sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tauri-apps/cli@2.11.4': + resolution: {integrity: sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==} + engines: {node: '>= 10'} + hasBin: true + + '@tauri-apps/plugin-autostart@2.5.1': + resolution: {integrity: sha512-zS/xx7yzveCcotkA+8TqkI2lysmG2wvQXv2HGAVExITmnFfHAdj1arGsbbfs3o6EktRHf6l34pJxc3YGG2mg7w==} + + '@tauri-apps/plugin-opener@2.5.5': + resolution: {integrity: sha512-xvzGai5aQds8j8R8RsUK/lW6pGG50YgOYIPLzvkqmkwAj7dfySOD7sGtejRzvVdMmv1EQfKVEFh1MvmDp8QR0g==} + + '@tiptap/core@3.31.0': + resolution: {integrity: sha512-SY3yE2ACharFvk0BKgotV6SbwV8R4tN5fGtWOM6deY4JNF3v6CXOD78RFwjs2HSohGWBRyJVPDvrX9nfi8Nsxg==} + peerDependencies: + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-blockquote@3.31.0': + resolution: {integrity: sha512-E1EcYp28NOEKLkMqf9Z4t/ZjVO5hOX/c5r9nu/sfp5+KePj4TaxGDcruijP5DYJuKxlsZx2lr6/lldiK9KilXw==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-bold@3.31.0': + resolution: {integrity: sha512-KB3V3/8/XSrXNM0m7Svgg8xjwG/mr9nYh0krGznmDK1eArxj2GQd5h2g0QiBy8e+G//NMEbWQxSKbhKUKz7aoQ==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-bubble-menu@3.31.0': + resolution: {integrity: sha512-CNuyfYH2KfcREASRh280UkGqYTwuKlMNcyBR7KsZrpzAVwFVo9D6qMkE6/q7I8mNMt2+/Xebe9UFZIYnVccCJA==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-bullet-list@3.31.0': + resolution: {integrity: sha512-jLrzWDzBhfeOK/idnrpyncTuRamBNGCscDC7omG1S+CD3CFuDrp5Ws2tcv5fgASt2Vc/f9Y9zC+gOPkeIEpYFA==} + peerDependencies: + '@tiptap/extension-list': 3.31.0 + + '@tiptap/extension-code-block@3.31.0': + resolution: {integrity: sha512-y/A6N96gBPjEHqVYZ3u/yO/Anyo92iDWgYqShMXAvHprn2Ipmn32wIkX05Kv983fa6j6ySXF7A1nJdHcGCl5aQ==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-code@3.31.0': + resolution: {integrity: sha512-QWo9oItNzyzNMXoCOG4/PiCNYoTjhUKtI5NE7iRJhd9op3KsLoyIO+og+P+0ORxG59Ic76SkXl2/lILaVsB48g==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-document@3.31.0': + resolution: {integrity: sha512-C2eDFoBlcRAb4sgbc9ZH+mrFFbjIS9VyoERGeDum3QRdqLEKU1h14Th7isHpGQzTvTywhXQxqXC64mgeSGXLsA==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-dropcursor@3.31.0': + resolution: {integrity: sha512-hIZOPfM0sGkAhPV69IK1zWMbWBd3wmhS0XCKBRcXUWrE2MW4kkaSyYprSFqtqFuUa3lVIEFrlME1WOEBJ2/2rg==} + peerDependencies: + '@tiptap/extensions': 3.31.0 + + '@tiptap/extension-floating-menu@3.31.0': + resolution: {integrity: sha512-NFwv1Kah3zGLmjj53Z+pA2584GdgMDClgvebJPqSunsSdHwHlFwfu+Op1u73xuEYOd6wGgCgzzDZ3V4CKwkJqg==} + peerDependencies: + '@floating-ui/dom': ^1.0.0 + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-gapcursor@3.31.0': + resolution: {integrity: sha512-TY1S9D9x86iIHiPiDU4KtRmLYTgE6RKa54jXubFyekUYFmgNfHuUunWmpK/Rvy6tkG1CKHNL7X+wdo8QXi+hcw==} + peerDependencies: + '@tiptap/extensions': 3.31.0 + + '@tiptap/extension-hard-break@3.31.0': + resolution: {integrity: sha512-e3e3YwTSxzO1bYalphs3A3zr+nYRWASMKFPzviO0C2P7vKE4kT4rvCYCWuUxIpfit8Vnh6TFVIkhrQ/6IFlDzw==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-heading@3.31.0': + resolution: {integrity: sha512-ryQnvq2iUW2IwjbLniGlRZliNA5YserRTX2mCKyQcv8z/u062J01JDLlX73o47tu4DjuBzmkx6t5SsiXNODRcg==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-horizontal-rule@3.31.0': + resolution: {integrity: sha512-RTKLnuEmGJOlxzLOmKo85hstXfuLlg+zQSKAggdD6BrSTDR5fdPZXnZTb/gDCo6chCR5BoKHu7Xv1spnPzRmEw==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-italic@3.31.0': + resolution: {integrity: sha512-8Top75aMS9feCtKYADWOYLrJjx3SGvazETWZdEwDYDGZtEOB7q2+FxcRu2xi+K6Lx9LhzxS9ChULtNdBuRevkQ==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-link@3.31.0': + resolution: {integrity: sha512-arzZYxyf6in2BzdB3GR53VhSlE3ZwmY8CflXRo1xHpSpxq393myLP6FnaxLPxcfcScfOOsaYh2KAED0HJoBZfA==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-list-item@3.31.0': + resolution: {integrity: sha512-ayZa+KEMigJis0s8n3X3pqRxzsMuom9JoqE6byJ5NP2oaI2HOQ95yfcuILNKukg1nXG8vkgifEsXxjXz4O2o2g==} + peerDependencies: + '@tiptap/extension-list': 3.31.0 + + '@tiptap/extension-list-keymap@3.31.0': + resolution: {integrity: sha512-bmZZmQyH0PibpIUA2thA3Cgm82cnrcC4oU4eNqiaoUqrva1NuOIqb71L2Br9keBnmtQKEtkzdODs4x+eI53kBg==} + peerDependencies: + '@tiptap/extension-list': 3.31.0 + + '@tiptap/extension-list@3.31.0': + resolution: {integrity: sha512-cED2TPewS3CtzZUdCZNG/qbc6vHJunKRzkFiT8x3RF+pmtIQlrPRH5Y9sbkzLt3yg0ua5KMGFtBH37AHaKUFDw==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-ordered-list@3.31.0': + resolution: {integrity: sha512-KLCax7pI/3JTCkMY01n2+CXwZRPPsIVR1fvSmkFMryUPonkkMYWSUlXCuYKvF22+kM0gU0kiXsVjqsn/YFdeDg==} + peerDependencies: + '@tiptap/extension-list': 3.31.0 + + '@tiptap/extension-paragraph@3.31.0': + resolution: {integrity: sha512-78l9D/jpKvvWXrBBzYPfgqwonBY76+1MMgSovkPffIbya+jaYDJdZhIpeCZXtSBDnw8cYE/rheAeusN1QG3SVQ==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-placeholder@3.31.0': + resolution: {integrity: sha512-zH1Z8xZX72P7wZ3HD6fUGJ7XTVWMgyN5Sy+OiD3tuuxfr/oUvS6ykaoGBdHGTMBdrXZpp+GfAcch1MrvLGn8oA==} + peerDependencies: + '@tiptap/extensions': 3.31.0 + + '@tiptap/extension-strike@3.31.0': + resolution: {integrity: sha512-M1X94ve0TNU7LrHCDn6nKf6vM8PEaKQt3FLTwhTIw5uTbx2Imw+uyRrySDdxLWHY/ul8x17I2w9MVLqK/D+Zvw==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-task-item@3.31.0': + resolution: {integrity: sha512-hGodDipjRCOsZjffTuChLWIzJLrxvPI5VI7rYHyN40qRCR3Re9OgO52iJCJxR+W2+ZBSLZi4fSYsYf6L9VYVHA==} + peerDependencies: + '@tiptap/extension-list': 3.31.0 + + '@tiptap/extension-task-list@3.31.0': + resolution: {integrity: sha512-82Scqfcwi8ZZ4bAKeOa0CUwuwsQKFduh0ZRu27P/BdUJIcvMTxV5x9t+JWnumBUZunBixs6EIg4P/4dY1R60LQ==} + peerDependencies: + '@tiptap/extension-list': 3.31.0 + + '@tiptap/extension-text@3.31.0': + resolution: {integrity: sha512-K+8nIKgNNBiL1+AarcuzvPoRpjuFk5qOe9MM/n9O3HyTTLeWNfRV/zFifcpSoHmhf0QvEAXqmnxDiIpyCpy9+g==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extension-underline@3.31.0': + resolution: {integrity: sha512-B4VK5nu0UdKl7OiMvIcjDPMYmhe8+v1B763Is/GzPSJOMstUHRqAqPx2AsPla6A4UGuZwTx//ajVa5QAGionZQ==} + peerDependencies: + '@tiptap/core': 3.31.0 + + '@tiptap/extensions@3.31.0': + resolution: {integrity: sha512-WOv9Y1VjhzAYycrIGPygwmLkoO/UdFYFE5uDWN+FJWMui5sLmLrmfKtOOYzeWIevxomFGix/wl5hqaVGIev1+g==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + + '@tiptap/pm@3.31.0': + resolution: {integrity: sha512-JGPUSPLDFPjgFZHcNSghVJmEfvYp2SRPZQ1K8AZOuviEsUNvkYmJsBdudeYwUYNzYO38L4Yy0G6UMSuniBDIMg==} + + '@tiptap/react@3.31.0': + resolution: {integrity: sha512-s3NfUSqXfkGpjQ6iL8In7jdTIlIdZ+Ll4bO30C54kokiNq6t8mct7JPt3qGeCCeH3FTqNMLzaBDAU8EfcZw+PQ==} + peerDependencies: + '@tiptap/core': 3.31.0 + '@tiptap/pm': 3.31.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tiptap/starter-kit@3.31.0': + resolution: {integrity: sha512-P80nhDEsBf6EMTaRb/ld3BaQW7iqITPR6BiIjOjbO3QDivCz/rGDKSS6q4ngsXfXOpSBc/clo/g0OyAdA8+Q4Q==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/linkify-it@3.0.5': + resolution: {integrity: sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@13.0.9': + resolution: {integrity: sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==} + + '@types/markdown-it@14.2.0': + resolution: {integrity: sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@1.0.5': + resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@26.4.1': + resolution: {integrity: sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA==} + + '@types/react-dom@19.2.5': + resolution: {integrity: sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + + '@vitejs/plugin-react@6.1.1': + resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + oxc-transform-react: ^0.145.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + oxc-transform-react: + optional: true + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + argparse@3.0.1: + resolution: {integrity: sha512-nM4mHF/KM1v59ZNKX7zfusQz5wUAxR511YG8Vo6TyiV4aqhu++rbJW4v04xsWhpSsHFj66flT8P7znVpyO20xQ==} + + autoprefixer@10.5.4: + resolution: {integrity: sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + baseline-browser-mapping@2.11.20: + resolution: {integrity: sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.28.8: + resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + electron-to-chromium@1.5.420: + resolution: {integrity: sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==} + + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-equals@5.4.1: + resolution: {integrity: sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==} + engines: {node: '>=6.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + html-parse-stringify@4.0.1: + resolution: {integrity: sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + i18next@26.4.1: + resolution: {integrity: sha512-9YbX5E6gd1H+yaOSX3izCsPj5iWXyH7X4oC+iuHHJJw8AeHglzOK5SJf+1CHxaYKYigcea+8jvzSxqYx46YvyA==} + peerDependencies: + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + typescript: + optional: true + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} + + linkify-it@6.1.0: + resolution: {integrity: sha512-wJ/TwpSDTLepCrQoYWYIExIKg5Zchex2Nn5yk2mFnB+6PtdkHtyLx742md9csRjjOnGkKIS/RrbY7l8D6gT9Vw==} + + linkifyjs@4.3.3: + resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-it-task-lists@2.1.1: + resolution: {integrity: sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==} + + markdown-it@14.3.1: + resolution: {integrity: sha512-4Ej49aYTDFIQ+uBkfX8GBvJGccoARxxPep+7aWTs55ozbjQJpW9M26Fe53vnGgvLeVzva/amzjQQaQu9w0vMhA==} + hasBin: true + + markdown-it@15.0.1: + resolution: {integrity: sha512-9/7gE95FNPkfUWrjJIoHZza2iLmuJlPD0UNMxPi7bxUrbCR525YZY0r+zyfes0dZI5ZZ/uNIXUJca0pJvtw41g==} + hasBin: true + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdurl@2.1.0: + resolution: {integrity: sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.54: + resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} + engines: {node: '>=18'} + + orderedmap@2.1.1: + resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + prosemirror-changeset@2.4.2: + resolution: {integrity: sha512-ViYrjMSg3YFiXwIhKaluu+/mi3Yrxt6AR8ri14ulTaGcZtXO1CThl7A2gv79qx5fQnOw8woKwyBU2u+9PVCm3w==} + + prosemirror-commands@1.7.2: + resolution: {integrity: sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw==} + + prosemirror-dropcursor@1.8.3: + resolution: {integrity: sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ==} + + prosemirror-gapcursor@1.4.1: + resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==} + + prosemirror-history@1.5.0: + resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} + + prosemirror-inputrules@1.5.1: + resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} + + prosemirror-keymap@1.2.3: + resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} + + prosemirror-markdown@1.13.7: + resolution: {integrity: sha512-NV57Oxe3fXR+bNGXS5XW4sKt/XGIyY3n0eHVUoJORcismeF4FFMpLBnl0YESy2BvJQHloexX+9P+zT3VqFj/CQ==} + + prosemirror-model@1.25.11: + resolution: {integrity: sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ==} + + prosemirror-schema-list@1.5.1: + resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} + + prosemirror-state@1.4.4: + resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} + + prosemirror-tables@1.8.5: + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} + + prosemirror-transform@1.12.1: + resolution: {integrity: sha512-t4F5615FycnCqsX7ShTUs8+jfnwcf46kuFRvSl/3qFx2QTZ4DjgowesLHQXcFP7G//TjesqZG3WtgL7vdyVJyA==} + + prosemirror-view@1.42.3: + resolution: {integrity: sha512-oTN7EtH+CpwxU9NrwEYWd0UZ4JUx7l048l5A2Xppm4p/60isZYLnth9QVQmC3VRIvdrIWCxwZSd+Uz791G31/w==} + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + react-dom@19.2.8: + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} + peerDependencies: + react: ^19.2.8 + + react-i18next@17.0.13: + resolution: {integrity: sha512-Cc1PscmblIHA1kljTqDwrcVMI21ydgmUzw0UAeQBe7pAOgfuRLfzXze4EUBQoeDiICzFIXXhHFoZxuetNg5D0Q==} + peerDependencies: + i18next: '>= 26.2.0' + react: '>= 16.8.0' + react-dom: '*' + react-native: '*' + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + typescript: + optional: true + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react@19.2.8: + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + rolldown@1.2.6: + resolution: {integrity: sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rope-sequence@1.3.4: + resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tiptap-markdown@0.9.0: + resolution: {integrity: sha512-dKLQ9iiuGNgrlGVjrNauF/UBzWu4LYOx5pkD0jNkmQt/GOwfCJsBuzZTsf1jZ204ANHOm572mZ9PYvGh1S7tpQ==} + peerDependencies: + '@tiptap/core': ^3.0.1 + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + uc.micro@3.0.0: + resolution: {integrity: sha512-U3PppEkleoTnIfi8BozMx3yju3qc/L6SwqWo2Sw+54PX+PX0q9I+r1Um5HCmqD7n9VDX5/v3vQH/AjA6deDdtw==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + update-browserslist-db@1.3.2: + resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@8.2.2: + resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 || ^0.5.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + zustand@5.0.15: + resolution: {integrity: sha512-MpSEjRiBkA9crSYeOUH32rJC7SVqAbm0Fqcqge/bUi2PPoLcBWKOsG+C8mevmpr8TwXHBVkChbbJiyvkE+i/3A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@babel/runtime@7.29.7': {} + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + optional: true + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + optional: true + + '@floating-ui/utils@0.2.12': + optional: true + + '@fontsource/fira-code@5.3.0': {} + + '@fontsource/google-sans@5.3.1': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@oxc-project/types@0.147.0': {} + + '@phosphor-icons/react@2.1.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@rolldown/binding-android-arm-eabi@1.2.6': + optional: true + + '@rolldown/binding-android-arm64@1.2.6': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.6': + optional: true + + '@rolldown/binding-darwin-x64@1.2.6': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.6': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.6': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.6': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.6': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.6': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.6': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.6': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.6': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.6': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.6': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.6': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@tailwindcss/node@4.3.3': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.24.5 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.3 + + '@tailwindcss/oxide-android-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.3': + optional: true + + '@tailwindcss/oxide@4.3.3': + 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 + + '@tailwindcss/vite@4.3.3(vite@8.2.2(@types/node@26.4.1)(jiti@2.7.0))': + dependencies: + '@tailwindcss/node': 4.3.3 + '@tailwindcss/oxide': 4.3.3 + tailwindcss: 4.3.3 + vite: 8.2.2(@types/node@26.4.1)(jiti@2.7.0) + + '@tauri-apps/api@2.11.1': {} + + '@tauri-apps/cli-darwin-arm64@2.11.4': + optional: true + + '@tauri-apps/cli-darwin-x64@2.11.4': + optional: true + + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': + optional: true + + '@tauri-apps/cli-linux-arm64-gnu@2.11.4': + optional: true + + '@tauri-apps/cli-linux-arm64-musl@2.11.4': + optional: true + + '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': + optional: true + + '@tauri-apps/cli-linux-x64-gnu@2.11.4': + optional: true + + '@tauri-apps/cli-linux-x64-musl@2.11.4': + optional: true + + '@tauri-apps/cli-win32-arm64-msvc@2.11.4': + optional: true + + '@tauri-apps/cli-win32-ia32-msvc@2.11.4': + optional: true + + '@tauri-apps/cli-win32-x64-msvc@2.11.4': + optional: true + + '@tauri-apps/cli@2.11.4': + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.11.4 + '@tauri-apps/cli-darwin-x64': 2.11.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.11.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.11.4 + '@tauri-apps/cli-linux-arm64-musl': 2.11.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.11.4 + '@tauri-apps/cli-linux-x64-gnu': 2.11.4 + '@tauri-apps/cli-linux-x64-musl': 2.11.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.11.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.11.4 + '@tauri-apps/cli-win32-x64-msvc': 2.11.4 + + '@tauri-apps/plugin-autostart@2.5.1': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@tauri-apps/plugin-opener@2.5.5': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@tiptap/core@3.31.0(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-blockquote@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-bold@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-bubble-menu@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@floating-ui/dom': 1.8.0 + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + optional: true + + '@tiptap/extension-bullet-list@3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-code-block@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-code@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-document@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-dropcursor@3.31.0(@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extensions': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-floating-menu@3.31.0(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@floating-ui/dom': 1.8.0 + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + optional: true + + '@tiptap/extension-gapcursor@3.31.0(@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extensions': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-hard-break@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-heading@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-horizontal-rule@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-italic@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-link@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + linkifyjs: 4.3.3 + + '@tiptap/extension-list-item@3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-list-keymap@3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + + '@tiptap/extension-ordered-list@3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-paragraph@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-placeholder@3.31.0(@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extensions': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-strike@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-task-item@3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-task-list@3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + + '@tiptap/extension-text@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extension-underline@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + + '@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + + '@tiptap/pm@3.31.0': + dependencies: + prosemirror-changeset: 2.4.2 + prosemirror-commands: 1.7.2 + prosemirror-dropcursor: 1.8.3 + prosemirror-gapcursor: 1.4.1 + prosemirror-history: 1.5.0 + prosemirror-inputrules: 1.5.1 + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.11 + prosemirror-schema-list: 1.5.1 + prosemirror-state: 1.4.4 + prosemirror-tables: 1.8.5 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + '@tiptap/react@3.31.0(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)(@types/react-dom@19.2.5(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + '@types/react': 19.2.18 + '@types/react-dom': 19.2.5(@types/react@19.2.18) + '@types/use-sync-external-store': 0.0.6 + fast-equals: 5.4.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + '@tiptap/extension-bubble-menu': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/extension-floating-menu': 3.31.0(@floating-ui/dom@1.8.0)(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + transitivePeerDependencies: + - '@floating-ui/dom' + + '@tiptap/starter-kit@3.31.0': + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@tiptap/extension-blockquote': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/extension-bold': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-bullet-list': 3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-code': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-code-block': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/extension-document': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-dropcursor': 3.31.0(@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-gapcursor': 3.31.0(@tiptap/extensions@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-hard-break': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-heading': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-horizontal-rule': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/extension-italic': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-link': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/extension-list': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/extension-list-item': 3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-list-keymap': 3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-ordered-list': 3.31.0(@tiptap/extension-list@3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0)) + '@tiptap/extension-paragraph': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-strike': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-text': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extension-underline': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)) + '@tiptap/extensions': 3.31.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0))(@tiptap/pm@3.31.0) + '@tiptap/pm': 3.31.0 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/linkify-it@3.0.5': {} + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@13.0.9': + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + + '@types/markdown-it@14.2.0': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@1.0.5': {} + + '@types/mdurl@2.0.0': {} + + '@types/ms@2.1.0': {} + + '@types/node@26.4.1': + dependencies: + undici-types: 8.3.0 + + '@types/react-dom@19.2.5(@types/react@19.2.18)': + dependencies: + '@types/react': 19.2.18 + + '@types/react@19.2.18': + dependencies: + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/use-sync-external-store@0.0.6': {} + + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@ungap/structured-clone@1.4.0': {} + + '@vitejs/plugin-react@6.1.1(vite@8.2.2(@types/node@26.4.1)(jiti@2.7.0))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.2.2(@types/node@26.4.1)(jiti@2.7.0) + + argparse@2.0.1: {} + + argparse@3.0.1: {} + + autoprefixer@10.5.4(postcss@8.5.26): + dependencies: + browserslist: 4.28.8 + caniuse-lite: 1.0.30001810 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.26 + postcss-value-parser: 4.2.0 + + bail@2.0.2: {} + + baseline-browser-mapping@2.11.20: {} + + browserslist@4.28.8: + dependencies: + baseline-browser-mapping: 2.11.20 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.420 + node-releases: 2.0.54 + update-browserslist-db: 1.3.2(browserslist@4.28.8) + + caniuse-lite@1.0.30001810: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + clsx@2.1.1: {} + + comma-separated-tokens@2.0.3: {} + + csstype@3.2.3: {} + + date-fns@4.4.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + electron-to-chromium@1.5.420: {} + + enhanced-resolve@5.24.5: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@4.5.0: {} + + entities@8.0.0: {} + + escalade@3.2.0: {} + + escape-string-regexp@5.0.0: {} + + estree-util-is-identifier-name@3.0.0: {} + + extend@3.0.2: {} + + fast-equals@5.4.1: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fraction.js@5.3.4: {} + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + html-parse-stringify@4.0.1: {} + + html-url-attributes@3.0.1: {} + + i18next@26.4.1(typescript@7.0.2): + optionalDependencies: + typescript: 7.0.2 + + inline-style-parser@0.2.7: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-hexadecimal@2.0.1: {} + + is-plain-obj@4.1.0: {} + + jiti@2.7.0: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + 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 + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + linkify-it@5.0.2: + dependencies: + uc.micro: 2.1.0 + + linkify-it@6.1.0: + dependencies: + uc.micro: 3.0.0 + + linkifyjs@4.3.3: {} + + longest-streak@3.1.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + markdown-it-task-lists@2.1.1: {} + + markdown-it@14.3.1: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.2 + mdurl: 2.1.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + markdown-it@15.0.1: + dependencies: + argparse: 3.0.1 + entities: 8.0.0 + linkify-it: 6.1.0 + mdurl: 2.1.0 + punycode.js: 2.3.1 + uc.micro: 3.0.0 + + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdurl@2.1.0: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + node-releases@2.0.54: {} + + orderedmap@2.1.1: {} + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss-value-parser@4.2.0: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-information@7.2.0: {} + + prosemirror-changeset@2.4.2: + dependencies: + prosemirror-transform: 1.12.1 + + prosemirror-commands@1.7.2: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + prosemirror-dropcursor@1.8.3: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + prosemirror-gapcursor@1.4.1: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-view: 1.42.3 + + prosemirror-history@1.5.0: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + rope-sequence: 1.3.4 + + prosemirror-inputrules@1.5.1: + dependencies: + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + prosemirror-keymap@1.2.3: + dependencies: + prosemirror-state: 1.4.4 + w3c-keyname: 2.2.8 + + prosemirror-markdown@1.13.7: + dependencies: + '@types/markdown-it': 14.2.0 + markdown-it: 14.3.1 + prosemirror-model: 1.25.11 + + prosemirror-model@1.25.11: + dependencies: + orderedmap: 2.1.1 + + prosemirror-schema-list@1.5.1: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + prosemirror-state@1.4.4: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + prosemirror-tables@1.8.5: + dependencies: + prosemirror-keymap: 1.2.3 + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + prosemirror-view: 1.42.3 + + prosemirror-transform@1.12.1: + dependencies: + prosemirror-model: 1.25.11 + + prosemirror-view@1.42.3: + dependencies: + prosemirror-model: 1.25.11 + prosemirror-state: 1.4.4 + prosemirror-transform: 1.12.1 + + punycode.js@2.3.1: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-i18next@17.0.13(i18next@26.4.1(typescript@7.0.2))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(typescript@7.0.2): + dependencies: + '@babel/runtime': 7.29.7 + html-parse-stringify: 4.0.1 + i18next: 26.4.1(typescript@7.0.2) + react: 19.2.8 + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + react-dom: 19.2.8(react@19.2.8) + typescript: 7.0.2 + + react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8): + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/react': 19.2.18 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.8 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react@19.2.8: {} + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + rolldown@1.2.6: + dependencies: + '@oxc-project/types': 0.147.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.6 + '@rolldown/binding-android-arm64': 1.2.6 + '@rolldown/binding-darwin-arm64': 1.2.6 + '@rolldown/binding-darwin-x64': 1.2.6 + '@rolldown/binding-freebsd-x64': 1.2.6 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.6 + '@rolldown/binding-linux-arm64-gnu': 1.2.6 + '@rolldown/binding-linux-arm64-musl': 1.2.6 + '@rolldown/binding-linux-ppc64-gnu': 1.2.6 + '@rolldown/binding-linux-s390x-gnu': 1.2.6 + '@rolldown/binding-linux-x64-gnu': 1.2.6 + '@rolldown/binding-linux-x64-musl': 1.2.6 + '@rolldown/binding-openharmony-arm64': 1.2.6 + '@rolldown/binding-win32-arm64-msvc': 1.2.6 + '@rolldown/binding-win32-x64-msvc': 1.2.6 + + rope-sequence@1.3.4: {} + + scheduler@0.27.0: {} + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.3: {} + + tapable@2.3.3: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tiptap-markdown@0.9.0(@tiptap/core@3.31.0(@tiptap/pm@3.31.0)): + dependencies: + '@tiptap/core': 3.31.0(@tiptap/pm@3.31.0) + '@types/markdown-it': 13.0.9 + markdown-it: 14.3.1 + markdown-it-task-lists: 2.1.1 + prosemirror-markdown: 1.13.7 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + uc.micro@2.1.0: {} + + uc.micro@3.0.0: {} + + undici-types@8.3.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + update-browserslist-db@1.3.2(browserslist@4.28.8): + dependencies: + browserslist: 4.28.8 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@8.2.2(@types/node@26.4.1)(jiti@2.7.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.26 + rolldown: 1.2.6 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.4.1 + fsevents: 2.3.3 + jiti: 2.7.0 + + w3c-keyname@2.2.8: {} + + zustand@5.0.15(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)): + optionalDependencies: + '@types/react': 19.2.18 + react: 19.2.8 + use-sync-external-store: 1.6.0(react@19.2.8) + + zwitch@2.0.4: {} diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..502406b --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,4 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +/gen/schemas diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock new file mode 100644 index 0000000..d0412a0 --- /dev/null +++ b/src-tauri/Cargo.lock @@ -0,0 +1,5203 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auto-launch" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471" +dependencies = [ + "dirs 4.0.0", + "thiserror 1.0.69", + "winreg 0.10.1", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.1", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.20", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.1", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.1", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.1", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.5+spec-1.1.0", + "vswhom", + "winreg 0.55.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" +dependencies = [ + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide 0.9.1", + "zlib-rs", +] + +[[package]] +name = "floatick" +version = "0.4.0" +dependencies = [ + "chrono", + "dirs 6.0.0", + "log", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-autostart", + "tauri-plugin-log", + "tauri-plugin-opener", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-executor" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" + +[[package]] +name = "icu_properties" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" + +[[package]] +name = "icu_provider" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ipnet" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "js-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.1", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8f1ea3f21fd3405dcaf6c9b5c1630af9afc422d9073ea39c5f6d6c772e08ed" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.1", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.1", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.1", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.1", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.1", + "block2", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.1", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.1", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "open" +version = "5.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c603ab8300cf18bc3b14146b19fe3dfcc4843ae5a400cd0e7a30b95aa366634" +dependencies = [ + "dunce", + "is-wsl", + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "plist" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.1", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.13+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.1", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.20", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.119", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.13.1", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.1", + "jiff", + "schemars 0.9.0", + "schemars 1.2.2", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "swift-rs" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45c444e496845d3f2a351146bff59aae4975b2280238df1dfaa0c7d1846f38e" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.13.1", + "block2", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs 6.0.0", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "image", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.20", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs 6.0.0", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.119", + "tauri-utils", + "thiserror 2.0.20", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-autostart" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459383cebc193cdd03d1ba4acc40f2c408a7abce419d64bdcd2d745bc2886f70" +dependencies = [ + "auto-launch", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", +] + +[[package]] +name = "tauri-plugin-log" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4e8861142c21636b03ff6eb9682a073814112e35220435670738a8be7b49896" +dependencies = [ + "android_logger", + "fern", + "log", + "objc2", + "objc2-foundation", + "serde", + "serde_json", + "serde_repr", + "swift-rs", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "time", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d60366174b745b4ef5824b8bbc1c457fd08f0ce101ff643c0a49181a9f4e91" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation", + "open", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.20", + "url", + "windows", + "zbus", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.20", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dom_query", + "dunce", + "glob", + "http", + "infer", + "json-patch", + "log", + "memchr", + "phf", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.20", + "toml 1.1.5+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.5+spec-1.1.0", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" +dependencies = [ + "new_debug_unreachable", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-util" +version = "0.7.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.1", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785" +dependencies = [ + "indexmap 2.14.1", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.4", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.1", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.1", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap 2.14.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.4", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow 1.0.4", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.1", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" +dependencies = [ + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.20", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs 6.0.0", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.20", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.4", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.4", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" +dependencies = [ + "serde", + "winnow 1.0.4", + "zvariant", +] + +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.4", +] + +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zvariant" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 1.0.4", + "zcheapstr", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 3.0.4", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 3.0.4", + "winnow 1.0.4", +] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..dcd5a0f --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "floatick" +version = "0.4.0" +description = "A focused, local-first floating todo and note app for macOS" +authors = ["Lucas"] +edition = "2021" +rust-version = "1.77.2" + +[lib] +name = "floatick_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2.6.3", features = [] } + +[dependencies] +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } +log = "0.4" +tauri = { version = "2.11.3", features = ["macos-private-api", "tray-icon", "image-png"] } +tauri-plugin-log = "2" +tauri-plugin-autostart = "2" +tauri-plugin-opener = "2" +dirs = "6.0" +chrono = { version = "0.4", features = ["serde"] } diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..795b9b7 --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..05ee128 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,13 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "enables the default permissions", + "windows": [ + "main" + ], + "permissions": [ + "core:default", + "opener:default", + "autostart:default" + ] +} diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000..a2f779f Binary files /dev/null and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..acfc749 Binary files /dev/null and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000..880baab Binary files /dev/null and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/64x64.png b/src-tauri/icons/64x64.png new file mode 100644 index 0000000..9051171 Binary files /dev/null and b/src-tauri/icons/64x64.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..adcb214 Binary files /dev/null and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..2f5bb2f Binary files /dev/null and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..226373e Binary files /dev/null and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..e13fe3a Binary files /dev/null and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..8fc66da Binary files /dev/null and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..b86b133 Binary files /dev/null and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..ebfe348 Binary files /dev/null and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..7e3810d Binary files /dev/null and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/Square89x89Logo.png b/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..f92e6b1 Binary files /dev/null and b/src-tauri/icons/Square89x89Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..b911b74 Binary files /dev/null and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..2ffbf24 --- /dev/null +++ b/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..bfc772e Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..6c28028 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..e85b272 Binary files /dev/null and b/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..432c916 Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..e73908a Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..cb984fc Binary files /dev/null and b/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..05cab36 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..b640751 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..297f772 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..5c57c0d Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..cf64285 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..f6a13e5 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..f125ee5 Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..ba6657c Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..7310e4f Binary files /dev/null and b/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/src-tauri/icons/android/values/ic_launcher_background.xml b/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 0000000..ea9c223 --- /dev/null +++ b/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #fff + \ No newline at end of file diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns new file mode 100644 index 0000000..a56d107 Binary files /dev/null and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000..3503f41 Binary files /dev/null and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000..29ed6f2 Binary files /dev/null and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@1x.png b/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000..eafcb66 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000..7286052 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@2x.png b/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 0000000..7286052 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-20x20@3x.png b/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 0000000..c06b5ea Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@1x.png b/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 0000000..456329d Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000..a515d66 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@2x.png b/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000..a515d66 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-29x29@3x.png b/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000..2036ae1 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@1x.png b/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000..7286052 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000..68bd634 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@2x.png b/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 0000000..68bd634 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-40x40@3x.png b/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 0000000..47c63f3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-512@2x.png b/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 0000000..f25633f Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-60x60@2x.png b/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 0000000..47c63f3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-60x60@3x.png b/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 0000000..f16af9c Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/src-tauri/icons/ios/AppIcon-76x76@1x.png b/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 0000000..9400cf1 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/src-tauri/icons/ios/AppIcon-76x76@2x.png b/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 0000000..dc7afeb Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000..ca84ef3 Binary files /dev/null and b/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/src-tauri/icons/tray-icon.png b/src-tauri/icons/tray-icon.png new file mode 100644 index 0000000..36f78eb Binary files /dev/null and b/src-tauri/icons/tray-icon.png differ diff --git a/src-tauri/icons/tray-icon@2x.png b/src-tauri/icons/tray-icon@2x.png new file mode 100644 index 0000000..0c843e7 Binary files /dev/null and b/src-tauri/icons/tray-icon@2x.png differ diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs new file mode 100644 index 0000000..ad7bf70 --- /dev/null +++ b/src-tauri/src/commands.rs @@ -0,0 +1,107 @@ +use tauri::{AppHandle, Manager}; +use tauri_plugin_autostart::ManagerExt; + +use crate::models::{AppSettings, NoteItem, TagWorkspace, TodoItem}; +use crate::storage; + +#[tauri::command] +pub fn get_todos() -> Result, String> { + storage::load_todos() +} + +#[tauri::command] +pub fn save_todos(app_handle: AppHandle, todos: Vec) -> Result<(), String> { + let active_count = todos + .iter() + .filter(|t| t.completed_at.is_none() && t.archived_at.is_none()) + .count(); + crate::tray::update_tray_todo_count(&app_handle, active_count); + storage::save_todos(&todos) +} + +#[tauri::command] +pub fn update_tray_count(app_handle: AppHandle, count: usize) { + crate::tray::update_tray_todo_count(&app_handle, count); +} + +#[tauri::command] +pub fn get_tags() -> Result { + storage::load_tags() +} + +#[tauri::command] +pub fn save_tags(workspace: TagWorkspace) -> Result<(), String> { + storage::save_tags(&workspace) +} + +#[tauri::command] +pub fn get_notes() -> Result, String> { + storage::load_notes() +} + +#[tauri::command] +pub fn save_notes(notes: Vec) -> Result<(), String> { + storage::save_notes(¬es) +} + +#[tauri::command] +pub fn get_settings() -> Result { + storage::load_settings() +} + +#[tauri::command] +pub fn save_settings(settings: AppSettings) -> Result<(), String> { + storage::save_settings(&settings) +} + +#[tauri::command] +pub fn hide_window(app_handle: AppHandle) -> Result<(), String> { + if let Some(window) = app_handle.get_webview_window("main") { + crate::panel::hide_window(&window).map_err(|e| e.to_string())?; + } + Ok(()) +} + +#[tauri::command] +pub fn toggle_window(app_handle: AppHandle) -> Result<(), String> { + if let Some(window) = app_handle.get_webview_window("main") { + if window.is_visible().unwrap_or(false) { + crate::panel::hide_window(&window).map_err(|e| e.to_string())?; + } else { + crate::tray::show_window(&app_handle); + } + } + Ok(()) +} + +#[tauri::command] +pub fn set_always_on_top(app_handle: AppHandle, always_on_top: bool) -> Result<(), String> { + if let Some(window) = app_handle.get_webview_window("main") { + window.set_always_on_top(always_on_top).map_err(|e| e.to_string())?; + } + Ok(()) +} + +#[tauri::command] +pub fn is_autostart_enabled(app_handle: AppHandle) -> Result { + app_handle + .autolaunch() + .is_enabled() + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn set_autostart_enabled(app_handle: AppHandle, enabled: bool) -> Result<(), String> { + let autolaunch = app_handle.autolaunch(); + if enabled { + autolaunch.enable().map_err(|e| e.to_string())?; + } else { + autolaunch.disable().map_err(|e| e.to_string())?; + } + Ok(()) +} + +#[tauri::command] +pub fn quit_app(app_handle: AppHandle) { + app_handle.exit(0); +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..f3518ca --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,78 @@ +pub mod commands; +pub mod models; +mod panel; +pub mod storage; +pub mod tray; + +use tauri::{Manager, WindowEvent}; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) + .plugin(tauri_plugin_autostart::init( + tauri_plugin_autostart::MacosLauncher::LaunchAgent, + Some(vec!["--autostart"]), + )) + .setup(|app| { + if cfg!(debug_assertions) { + app.handle().plugin( + tauri_plugin_log::Builder::default() + .level(log::LevelFilter::Info) + .build(), + )?; + } + + // Setup macOS Menu Bar System Tray + tray::setup_tray(app.handle())?; + + // Blur / Click-outside handling & initial window position + if let Some(window) = app.get_webview_window("main") { + let is_autostart = std::env::args().any(|arg| arg == "--autostart"); + if !is_autostart { + let app_handle = app.handle().clone(); + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(120)); + let h = app_handle.clone(); + let _ = app_handle.run_on_main_thread(move || { + tray::show_window(&h); + }); + }); + } + + let w_clone = window.clone(); + window.on_window_event(move |event| { + if let WindowEvent::Focused(focused) = event { + panel::on_focus_changed(&w_clone, *focused); + } + }); + } + + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ + commands::get_todos, + commands::save_todos, + commands::get_tags, + commands::save_tags, + commands::get_notes, + commands::save_notes, + commands::get_settings, + commands::save_settings, + commands::hide_window, + commands::toggle_window, + commands::set_always_on_top, + commands::is_autostart_enabled, + commands::set_autostart_enabled, + commands::update_tray_count, + commands::quit_app, + ]) + .build(tauri::generate_context!()) + .expect("error while building tauri application") + .run(|app_handle, event| { + if let tauri::RunEvent::Reopen { .. } = event { + log::info!(target: "floatick::panel", "reopen"); + tray::show_window(app_handle); + } + }); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..9aa9b82 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,5 @@ +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + floatick_lib::run(); +} diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs new file mode 100644 index 0000000..99a9aa6 --- /dev/null +++ b/src-tauri/src/models.rs @@ -0,0 +1,109 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +fn default_true() -> bool { + true +} + +fn default_system() -> String { + "system".to_string() +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TodoItem { + pub id: String, + pub title: String, + #[serde(default)] + pub content: String, + pub created_at: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub started_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub completed_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub archived_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub due_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reminder_at: Option, + #[serde(default = "default_true")] + pub notify_at_deadline: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub deadline_notified_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reminder_notified_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub snoozed_until: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TodoTag { + pub id: String, + pub name: String, + #[serde(default)] + pub color_hex: String, + #[serde(default)] + pub color_value: Option, + #[serde(default)] + pub created_at: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TagWorkspace { + pub version: u32, + pub tags: Vec, + pub assignments: HashMap>, +} + +impl Default for TagWorkspace { + fn default() -> Self { + Self { + version: 1, + tags: Vec::new(), + assignments: HashMap::new(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct NoteItem { + pub id: String, + pub title: String, + #[serde(default)] + pub content: String, + pub created_at: String, + pub updated_at: String, + #[serde(default)] + pub tag_ids: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pinned_at: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub archived_at: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AppSettings { + #[serde(default = "default_system")] + pub theme: String, + #[serde(default = "default_system")] + pub language: String, + #[serde(default = "default_true")] + pub always_on_top: bool, + #[serde(default = "default_true")] + pub collapse_when_clicking_outside: bool, +} + +impl Default for AppSettings { + fn default() -> Self { + Self { + theme: default_system(), + language: default_system(), + always_on_top: true, + collapse_when_clicking_outside: true, + } + } +} diff --git a/src-tauri/src/panel.rs b/src-tauri/src/panel.rs new file mode 100644 index 0000000..82848b1 --- /dev/null +++ b/src-tauri/src/panel.rs @@ -0,0 +1,204 @@ +use std::sync::Mutex; +use std::time::Duration; +use tauri::WebviewWindow; + +const BLUR_SETTLE_DELAY: Duration = Duration::from_millis(150); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum TrayAction { + Show, + Hide, +} + +impl TrayAction { + fn for_visibility(visible: bool) -> Self { + if visible { + Self::Hide + } else { + Self::Show + } + } +} + +#[derive(Default)] +struct PanelState { + revision: u64, + tray_action: Option, +} + +impl PanelState { + fn cancel_pending_hide(&mut self) { + self.revision = self.revision.wrapping_add(1); + } + + fn request_blur_hide(&mut self) -> Option { + self.cancel_pending_hide(); + // The tray gesture owns visibility until mouse-up, including long presses. + self.tray_action.is_none().then_some(self.revision) + } + + fn can_hide(&self, revision: u64) -> bool { + self.revision == revision && self.tray_action.is_none() + } + + fn tray_pressed(&mut self, visible: bool) { + self.cancel_pending_hide(); + self.tray_action = Some(TrayAction::for_visibility(visible)); + } + + fn tray_released(&mut self, visible: bool) -> TrayAction { + self.cancel_pending_hide(); + self.tray_action + .take() + .unwrap_or_else(|| TrayAction::for_visibility(visible)) + } +} + +static STATE: Mutex = Mutex::new(PanelState { + revision: 0, + tray_action: None, +}); + +pub(crate) fn cancel_pending_hide() { + STATE.lock().unwrap().cancel_pending_hide(); +} + +pub(crate) fn tray_pressed(visible: bool) { + STATE.lock().unwrap().tray_pressed(visible); +} + +pub(crate) fn tray_released(visible: bool) -> TrayAction { + STATE.lock().unwrap().tray_released(visible) +} + +pub(crate) fn hide_window(window: &WebviewWindow) -> tauri::Result<()> { + cancel_pending_hide(); + window.hide() +} + +pub(crate) fn on_focus_changed(window: &WebviewWindow, focused: bool) { + log::info!(target: "floatick::panel", "focus={focused}"); + if focused { + cancel_pending_hide(); + return; + } + if !matches!(window.is_visible(), Ok(true)) { + return; + } + + let Some(revision) = STATE.lock().unwrap().request_blur_hide() else { + return; + }; + let window = window.clone(); + std::thread::spawn(move || { + // run_on_main_thread executes inline when already on the UI thread, so it + // cannot itself debounce AppKit's transient focus changes. + std::thread::sleep(BLUR_SETTLE_DELAY); + let w = window.clone(); + if let Err(error) = window.run_on_main_thread(move || { + if !STATE.lock().unwrap().can_hide(revision) { + return; + } + // Check live native state as well as the request revision: native + // focus may already have recovered before its event is delivered. + if !matches!(w.is_visible(), Ok(true)) || !matches!(w.is_focused(), Ok(false)) { + return; + } + let collapse = crate::storage::load_settings() + .map(|s| s.collapse_when_clicking_outside) + .unwrap_or(true); + if collapse { + log::info!(target: "floatick::panel", "hide: settled blur"); + if let Err(error) = hide_window(&w) { + log::warn!(target: "floatick::panel", "blur hide failed: {error}"); + } + } + }) { + log::warn!(target: "floatick::panel", "blur check dispatch failed: {error}"); + } + }); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn persistent_blur_can_close_the_panel() { + let mut state = PanelState::default(); + let request = state.request_blur_hide().unwrap(); + assert!(state.can_hide(request)); + } + + #[test] + fn reopening_cancels_a_hide_from_the_previous_visibility_cycle() { + let mut state = PanelState::default(); + let old_blur = state.request_blur_hide().unwrap(); + state.cancel_pending_hide(); // Show, even if focus has not arrived yet. + assert!(!state.can_hide(old_blur)); + } + + #[test] + fn focus_recovery_cancels_old_blur_but_a_new_blur_can_close() { + let mut state = PanelState::default(); + let old_blur = state.request_blur_hide().unwrap(); + state.cancel_pending_hide(); // Focused(true). + let new_blur = state.request_blur_hide().unwrap(); + assert!(!state.can_hide(old_blur)); + assert!(state.can_hide(new_blur)); + } + + #[test] + fn repeated_blur_only_keeps_the_latest_request() { + let mut state = PanelState::default(); + let first = state.request_blur_hide().unwrap(); + let second = state.request_blur_hide().unwrap(); + assert!(!state.can_hide(first)); + assert!(state.can_hide(second)); + } + + #[test] + fn blur_before_tray_press_cannot_close_the_panel_mid_click() { + let mut state = PanelState::default(); + let blur = state.request_blur_hide().unwrap(); + state.tray_pressed(true); + assert!(!state.can_hide(blur)); + assert_eq!(state.tray_released(true), TrayAction::Hide); + } + + #[test] + fn blur_during_a_long_tray_press_cannot_reverse_a_close_into_an_open() { + let mut state = PanelState::default(); + state.tray_pressed(true); + assert_eq!(state.request_blur_hide(), None); + assert_eq!(state.request_blur_hide(), None); + // Even if native visibility changed, honor the intent at mouse-down. + assert_eq!(state.tray_released(false), TrayAction::Hide); + } + + #[test] + fn an_intervening_show_cannot_reverse_a_tray_open_into_a_close() { + let mut state = PanelState::default(); + state.tray_pressed(false); + state.cancel_pending_hide(); // For example, a concurrent Reopen event. + assert_eq!(state.tray_released(true), TrayAction::Show); + } + + #[test] + fn consecutive_tray_clicks_toggle_independently() { + let mut state = PanelState::default(); + state.tray_pressed(false); + assert_eq!(state.tray_released(false), TrayAction::Show); + state.tray_pressed(true); + assert_eq!(state.tray_released(true), TrayAction::Hide); + state.tray_pressed(false); + assert_eq!(state.tray_released(false), TrayAction::Show); + } + + #[test] + fn mouse_up_without_mouse_down_uses_current_visibility() { + let mut state = PanelState::default(); + assert_eq!(state.tray_released(false), TrayAction::Show); + assert_eq!(state.tray_released(true), TrayAction::Hide); + } +} diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs new file mode 100644 index 0000000..bdff5f7 --- /dev/null +++ b/src-tauri/src/storage.rs @@ -0,0 +1,150 @@ +use std::fs::{self, File}; +use std::io::Write; +use std::path::PathBuf; +use std::time::{SystemTime, UNIX_EPOCH}; + +use crate::models::{AppSettings, NoteItem, TagWorkspace, TodoItem}; + +const ROOT_DIR_NAME: &str = ".floatick"; +const TODOS_FILE: &str = "todos.json"; +const TAGS_FILE: &str = "tags.json"; +const NOTES_FILE: &str = "notes.json"; +const SETTINGS_FILE: &str = "settings.json"; + +pub fn get_storage_dir() -> Result { + let home = dirs::home_dir().ok_or_else(|| "Could not locate home directory".to_string())?; + let path = home.join(ROOT_DIR_NAME); + if !path.exists() { + fs::create_dir_all(&path).map_err(|e| format!("Failed to create storage directory: {e}"))?; + } + Ok(path) +} + +fn atomic_save(file_name: &str, data: &T) -> Result<(), String> { + let dir = get_storage_dir()?; + let target_path = dir.join(file_name); + + let ts = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_micros()) + .unwrap_or(0); + let tmp_path = dir.join(format!("{}.tmp-{}", file_name, ts)); + + let json_bytes = serde_json::to_vec_pretty(data) + .map_err(|e| format!("Serialization error for {file_name}: {e}"))?; + + { + let mut file = File::create(&tmp_path) + .map_err(|e| format!("Failed to create temp file {tmp_path:?}: {e}"))?; + file.write_all(&json_bytes) + .map_err(|e| format!("Failed to write to temp file: {e}"))?; + file.sync_all() + .map_err(|e| format!("Failed to sync temp file: {e}"))?; + } + + fs::rename(&tmp_path, &target_path) + .map_err(|e| format!("Failed to rename temp file to target {target_path:?}: {e}"))?; + + Ok(()) +} + +pub fn load_todos() -> Result, String> { + let dir = get_storage_dir()?; + let path = dir.join(TODOS_FILE); + if !path.exists() { + return Ok(Vec::new()); + } + let content = fs::read_to_string(&path) + .map_err(|e| format!("Failed to read {TODOS_FILE}: {e}"))?; + let items: Vec = serde_json::from_str(&content) + .map_err(|e| format!("Failed to parse {TODOS_FILE}: {e}"))?; + Ok(items) +} + +pub fn save_todos(todos: &[TodoItem]) -> Result<(), String> { + atomic_save(TODOS_FILE, &todos) +} + +pub fn load_tags() -> Result { + let dir = get_storage_dir()?; + let path = dir.join(TAGS_FILE); + if !path.exists() { + return Ok(TagWorkspace::default()); + } + let content = fs::read_to_string(&path) + .map_err(|e| format!("Failed to read {TAGS_FILE}: {e}"))?; + let mut workspace: TagWorkspace = serde_json::from_str(&content) + .map_err(|e| format!("Failed to parse {TAGS_FILE}: {e}"))?; + + for tag in &mut workspace.tags { + if tag.color_hex.is_empty() { + if let Some(val) = tag.color_value { + let rgb = val & 0x00FFFFFF; + tag.color_hex = format!("#{:06X}", rgb); + } else { + tag.color_hex = "#20B8A8".to_string(); + } + } + if tag.color_value.is_none() { + let clean = tag.color_hex.trim_start_matches('#'); + if let Ok(rgb) = i64::from_str_radix(clean, 16) { + tag.color_value = Some(0xFF000000i64 | (rgb & 0x00FFFFFF)); + } else { + tag.color_value = Some(4280334504); + } + } + } + Ok(workspace) +} + +pub fn save_tags(workspace: &TagWorkspace) -> Result<(), String> { + let mut to_save = workspace.clone(); + for tag in &mut to_save.tags { + if tag.color_value.is_none() { + let clean = tag.color_hex.trim_start_matches('#'); + if let Ok(rgb) = i64::from_str_radix(clean, 16) { + tag.color_value = Some(0xFF000000i64 | (rgb & 0x00FFFFFF)); + } else { + tag.color_value = Some(4280334504); + } + } + if tag.created_at.is_none() { + tag.created_at = Some(chrono::Utc::now().to_rfc3339()); + } + } + atomic_save(TAGS_FILE, &to_save) +} + +pub fn load_notes() -> Result, String> { + let dir = get_storage_dir()?; + let path = dir.join(NOTES_FILE); + if !path.exists() { + return Ok(Vec::new()); + } + let content = fs::read_to_string(&path) + .map_err(|e| format!("Failed to read {NOTES_FILE}: {e}"))?; + let notes: Vec = serde_json::from_str(&content) + .map_err(|e| format!("Failed to parse {NOTES_FILE}: {e}"))?; + Ok(notes) +} + +pub fn save_notes(notes: &[NoteItem]) -> Result<(), String> { + atomic_save(NOTES_FILE, ¬es) +} + +pub fn load_settings() -> Result { + let dir = get_storage_dir()?; + let path = dir.join(SETTINGS_FILE); + if !path.exists() { + return Ok(AppSettings::default()); + } + let content = fs::read_to_string(&path) + .map_err(|e| format!("Failed to read {SETTINGS_FILE}: {e}"))?; + let settings: AppSettings = serde_json::from_str(&content) + .map_err(|e| format!("Failed to parse {SETTINGS_FILE}: {e}"))?; + Ok(settings) +} + +pub fn save_settings(settings: &AppSettings) -> Result<(), String> { + atomic_save(SETTINGS_FILE, settings) +} diff --git a/src-tauri/src/tray.rs b/src-tauri/src/tray.rs new file mode 100644 index 0000000..ccdf1c1 --- /dev/null +++ b/src-tauri/src/tray.rs @@ -0,0 +1,364 @@ +use tauri::{ + menu::{Menu, MenuItem, PredefinedMenuItem}, + tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}, + AppHandle, LogicalPosition, Manager, Position, Rect, +}; + +pub fn setup_tray(app: &AppHandle) -> Result<(), Box> { + let show_item = MenuItem::with_id(app, "show", "显示 Floatick", true, None::<&str>)?; + let quit_item = MenuItem::with_id(app, "quit", "退出 Floatick", true, None::<&str>)?; + let sep = PredefinedMenuItem::separator(app)?; + + let menu = Menu::with_items(app, &[&show_item, &sep, &quit_item])?; + + let tray_icon = tauri::image::Image::from_bytes(include_bytes!("../icons/tray-icon@2x.png"))?; + + let initial_count = crate::storage::load_todos() + .map(|todos| { + todos + .iter() + .filter(|t| t.completed_at.is_none() && t.archived_at.is_none()) + .count() + }) + .unwrap_or(0); + + let title_str = if initial_count > 0 { + format!(" {}", if initial_count > 99 { "99+".to_string() } else { initial_count.to_string() }) + } else { + "".to_string() + }; + + let _tray = TrayIconBuilder::with_id("main-tray") + .icon(tray_icon) + .icon_as_template(true) + .title(title_str) + .menu(&menu) + .show_menu_on_left_click(false) + .tooltip("Floatick") + .on_menu_event(|app_handle, event| match event.id.as_ref() { + "show" => { + show_window(app_handle); + } + "quit" => { + app_handle.exit(0); + } + _ => {} + }) + .on_tray_icon_event(|tray, event| { + if let TrayIconEvent::Click { button, button_state, .. } = &event { + log::info!(target: "floatick::panel", "tray: {button:?} {button_state:?}"); + } + if let TrayIconEvent::Click { + button: MouseButton::Left, + button_state, + rect, + .. + } = event + { + let app_handle = tray.app_handle(); + match button_state { + MouseButtonState::Down => { + update_cached_tray_rect(rect); + if let Some(window) = app_handle.get_webview_window("main") { + crate::panel::tray_pressed(window.is_visible().unwrap_or(false)); + } + } + MouseButtonState::Up => toggle_window_at_rect(app_handle, rect), + } + } + }) + .build(app)?; + + Ok(()) +} + +use std::sync::RwLock; + +static LAST_TRAY_RECT: RwLock> = RwLock::new(None); + +pub fn update_cached_tray_rect(rect: Rect) { + let size = rect.size.to_logical::(1.0); + if size.width > 0.0 && size.height > 0.0 { + if let Ok(mut guard) = LAST_TRAY_RECT.write() { + *guard = Some(rect); + } + } +} + +pub fn get_cached_tray_rect() -> Option { + LAST_TRAY_RECT.read().ok().and_then(|guard| *guard) +} + +pub fn find_monitor_for_rect(window: &tauri::WebviewWindow, rect: &Rect) -> Option { + let monitors = window.available_monitors().ok()?; + if monitors.is_empty() { + return None; + } + if monitors.len() == 1 { + return monitors.into_iter().next(); + } + + // Pass 1: 2D check evaluating each monitor with its own scale factor + for m in &monitors { + let scale = m.scale_factor(); + let mon_pos = m.position().to_logical::(scale); + let mon_size = m.size().to_logical::(scale); + let tray_pos = rect.position.to_logical::(scale); + let tray_size = rect.size.to_logical::(scale); + + let tray_center_x = tray_pos.x + (tray_size.width / 2.0); + let tray_center_y = tray_pos.y + (tray_size.height / 2.0); + + let in_x = tray_center_x >= (mon_pos.x - 4.0) && tray_center_x <= (mon_pos.x + mon_size.width + 4.0); + let in_y = tray_center_y >= (mon_pos.y - 100.0) && tray_center_y <= (mon_pos.y + mon_size.height + 50.0); + + if in_x && in_y { + return Some(m.clone()); + } + } + + // Pass 2: Horizontal span check + for m in &monitors { + let scale = m.scale_factor(); + let mon_pos = m.position().to_logical::(scale); + let mon_size = m.size().to_logical::(scale); + let tray_pos = rect.position.to_logical::(scale); + let tray_size = rect.size.to_logical::(scale); + + let tray_center_x = tray_pos.x + (tray_size.width / 2.0); + if tray_center_x >= mon_pos.x && tray_center_x <= (mon_pos.x + mon_size.width) { + return Some(m.clone()); + } + } + + // Pass 3: Closest monitor center + let mut best_monitor = None; + let mut min_dist_sq = f64::MAX; + for m in &monitors { + let scale = m.scale_factor(); + let mon_pos = m.position().to_logical::(scale); + let mon_size = m.size().to_logical::(scale); + let tray_pos = rect.position.to_logical::(scale); + + let mon_center_x = mon_pos.x + (mon_size.width / 2.0); + let mon_center_y = mon_pos.y + (mon_size.height / 2.0); + let dx = tray_pos.x - mon_center_x; + let dy = tray_pos.y - mon_center_y; + let dist_sq = dx * dx + dy * dy; + if dist_sq < min_dist_sq { + min_dist_sq = dist_sq; + best_monitor = Some(m.clone()); + } + } + + best_monitor.or_else(|| window.primary_monitor().ok().flatten()) +} + +pub fn position_window_at_rect(window: &tauri::WebviewWindow, rect: Rect) { + update_cached_tray_rect(rect); + + let target_monitor = find_monitor_for_rect(window, &rect) + .or_else(|| window.primary_monitor().ok().flatten()) + .or_else(|| window.current_monitor().ok().flatten()); + + let scale_factor = target_monitor + .as_ref() + .map(|m| m.scale_factor()) + .unwrap_or_else(|| window.scale_factor().unwrap_or(2.0)); + + let window_width = 440.0; + let window_height = 700.0; + + let tray_pos = rect.position.to_logical::(scale_factor); + let tray_size = rect.size.to_logical::(scale_factor); + + let tray_center_x = tray_pos.x + (tray_size.width / 2.0); + let mut window_x = tray_center_x - (window_width / 2.0); + let mut window_y = if tray_size.height > 0.0 { + tray_pos.y + tray_size.height + 4.0 + } else { + 36.0 + }; + + if let Some(monitor) = target_monitor { + let mon_scale = monitor.scale_factor(); + let mon_pos = monitor.position().to_logical::(mon_scale); + let mon_size = monitor.size().to_logical::(mon_scale); + + let min_x = mon_pos.x + 8.0; + let max_x = mon_pos.x + mon_size.width - window_width - 8.0; + window_x = window_x.clamp(min_x, max_x); + + if tray_size.height > 0.0 { + let min_y = mon_pos.y + 8.0; + let max_y = mon_pos.y + mon_size.height - window_height - 8.0; + window_y = window_y.clamp(min_y, max_y); + } else { + window_y = mon_pos.y + 36.0; + } + } + + let _ = window.set_position(Position::Logical(LogicalPosition::new(window_x, window_y))); +} + +pub fn toggle_window_at_rect(app_handle: &AppHandle, rect: Rect) { + if let Some(window) = app_handle.get_webview_window("main") { + let is_visible = window.is_visible().unwrap_or(false); + let action = crate::panel::tray_released(is_visible); + log::info!(target: "floatick::panel", "tray action: {action:?}, visible={is_visible}"); + match action { + crate::panel::TrayAction::Hide => { + let _ = crate::panel::hide_window(&window); + } + crate::panel::TrayAction::Show => { + crate::panel::cancel_pending_hide(); + position_window_at_rect(&window, rect); + let _ = window.show(); + let _ = window.set_focus(); + } + } + } +} + +pub fn show_window(app_handle: &AppHandle) { + log::info!(target: "floatick::panel", "show requested"); + if let Some(window) = app_handle.get_webview_window("main") { + crate::panel::cancel_pending_hide(); + + let mut positioned = false; + if let Some(tray) = app_handle.tray_by_id("main-tray") { + if let Ok(Some(rect)) = tray.rect() { + let size = rect.size.to_logical::(1.0); + if size.width > 0.0 && size.height > 0.0 { + position_window_at_rect(&window, rect); + positioned = true; + } + } + } + + if !positioned { + if let Some(cached_rect) = get_cached_tray_rect() { + position_window_at_rect(&window, cached_rect); + positioned = true; + } + } + + if !positioned { + if let Ok(Some(monitor)) = window.primary_monitor().or_else(|_| window.current_monitor()) { + let scale = monitor.scale_factor(); + let mon_pos = monitor.position().to_logical::(scale); + let mon_size = monitor.size().to_logical::(scale); + let window_width = 440.0; + let window_x = mon_pos.x + mon_size.width - window_width - 20.0; + let window_y = mon_pos.y + 36.0; + let _ = window.set_position(Position::Logical(LogicalPosition::new(window_x, window_y))); + } + } + + let _ = window.show(); + let _ = window.set_focus(); + } +} + +pub fn update_tray_todo_count(app: &AppHandle, count: usize) { + if let Some(tray) = app.tray_by_id("main-tray") { + let title_str = if count > 0 { + format!(" {}", count) + } else { + "".to_string() + }; + let _ = tray.set_title(Some(title_str)); + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct MonitorBounds { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +pub fn match_monitor_index(monitors: &[MonitorBounds], tray_center: (f64, f64)) -> Option { + if monitors.is_empty() { + return None; + } + if monitors.len() == 1 { + return Some(0); + } + + let (cx, cy) = tray_center; + + // Pass 1: 2D check evaluating each monitor with generous margin for menu bar + for (i, m) in monitors.iter().enumerate() { + let in_x = cx >= (m.x - 4.0) && cx <= (m.x + m.width + 4.0); + let in_y = cy >= (m.y - 100.0) && cy <= (m.y + m.height + 50.0); + if in_x && in_y { + return Some(i); + } + } + + // Pass 2: Horizontal span check + for (i, m) in monitors.iter().enumerate() { + if cx >= m.x && cx <= (m.x + m.width) { + return Some(i); + } + } + + // Pass 3: Closest monitor center + let mut best = 0; + let mut min_dist_sq = f64::MAX; + for (i, m) in monitors.iter().enumerate() { + let mcx = m.x + (m.width / 2.0); + let mcy = m.y + (m.height / 2.0); + let dist_sq = (cx - mcx).powi(2) + (cy - mcy).powi(2); + if dist_sq < min_dist_sq { + min_dist_sq = dist_sq; + best = i; + } + } + Some(best) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_single_monitor() { + let monitors = vec![MonitorBounds { + x: 0.0, + y: 0.0, + width: 1920.0, + height: 1080.0, + }]; + assert_eq!(match_monitor_index(&monitors, (1500.0, 15.0)), Some(0)); + } + + #[test] + fn test_dual_monitor_left_vertical_right_horizontal() { + // User's multi-monitor setup: + // Screen 0 (Main/Right): (0, 0, 1920, 1080) + // Screen 1 (Vertical/Left): (-1080, -318, 1080, 1920) + let monitors = vec![ + MonitorBounds { + x: 0.0, + y: 0.0, + width: 1920.0, + height: 1080.0, + }, + MonitorBounds { + x: -1080.0, + y: -318.0, + width: 1080.0, + height: 1920.0, + }, + ]; + + // Click on right screen tray icon + assert_eq!(match_monitor_index(&monitors, (1639.5, 15.0)), Some(0)); + + // Click on left screen tray icon + assert_eq!(match_monitor_index(&monitors, (-200.0, -300.0)), Some(1)); + } +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..6998459 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,48 @@ +{ + "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", + "productName": "Floatick", + "version": "0.4.0", + "identifier": "io.github.lucaslushuo.floatick", + "build": { + "frontendDist": "../dist", + "devUrl": "http://localhost:1420", + "beforeDevCommand": "pnpm dev", + "beforeBuildCommand": "pnpm build" + }, + "app": { + "macOSPrivateApi": true, + "windows": [ + { + "label": "main", + "title": "Floatick", + "width": 440, + "height": 700, + "resizable": false, + "decorations": false, + "transparent": true, + "alwaysOnTop": true, + "skipTaskbar": true, + "visible": false, + "shadow": false + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], + "macOS": { + "bundleVersion": "12", + "minimumSystemVersion": "10.15" + } + } +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..7232410 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,206 @@ +import React, { useEffect, useState } from "react"; +import { Header } from "@/components/layout/Header"; +import { ContentSwitcher } from "@/components/layout/ContentSwitcher"; +import { TodoPanel } from "@/components/todos/TodoPanel"; +import { NotePanel } from "@/components/notes/NotePanel"; +import { SettingsDrawer } from "@/components/settings/SettingsDrawer"; +import { TagDrawer } from "@/components/tags/TagDrawer"; +import { TodoEditorDrawer } from "@/components/todos/TodoEditorDrawer"; +import { NoteEditorDrawer } from "@/components/notes/NoteEditorDrawer"; +import { useSettingsStore } from "@/stores/useSettingsStore"; +import { useTodoStore } from "@/stores/useTodoStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { useNoteStore } from "@/stores/useNoteStore"; +import { api } from "@/lib/api"; + +export const App: React.FC = () => { + const [activeTab, setActiveTab] = useState<"todos" | "notes">("todos"); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [isTagDrawerOpen, setIsTagDrawerOpen] = useState(false); + const [tagDrawerMode, setTagDrawerMode] = useState<"filter" | "assignment" | "manage">("filter"); + const [tagDrawerTargetTodoId, setTagDrawerTargetTodoId] = useState(null); + + const handleOpenTagFilter = () => { + setTagDrawerMode("filter"); + setTagDrawerTargetTodoId(null); + setIsTagDrawerOpen(true); + }; + + const handleOpenTagAssignment = (todoId: string) => { + setTagDrawerMode("assignment"); + setTagDrawerTargetTodoId(todoId); + setIsTagDrawerOpen(true); + }; + + const isTodoEditorOpen = useTodoStore((s) => s.isEditorOpen); + const setIsTodoEditorOpen = useTodoStore((s) => s.setIsEditorOpen); + const editingTodoId = useTodoStore((s) => s.editingTodoId); + + const isNoteEditorOpen = useNoteStore((s) => s.isEditorOpen); + const setIsNoteEditorOpen = useNoteStore((s) => s.setIsEditorOpen); + const editingNoteId = useNoteStore((s) => s.editingNoteId); + + const loadSettings = useSettingsStore((s) => s.loadSettings); + const loadTodos = useTodoStore((s) => s.loadTodos); + const loadTags = useTagStore((s) => s.loadTags); + const loadNotes = useNoteStore((s) => s.loadNotes); + + useEffect(() => { + // Initial data hydration from ~/.floatick + loadSettings(); + loadTodos(); + loadTags(); + loadNotes(); + + const params = new URLSearchParams(window.location.search); + if (params.get("tab") === "notes") { + setActiveTab("notes"); + } + if (params.get("mock") === "editor") { + setTimeout(() => { + useTodoStore.getState().setEditingTodoId("mock-todo-1"); + useTodoStore.getState().setEditorMode("edit"); + useTodoStore.getState().setIsEditorOpen(true); + }, 150); + } + if (params.get("mock") === "settings") { + setTimeout(() => { + setIsSettingsOpen(true); + }, 150); + } + if (params.get("mock") === "tags") { + setTimeout(() => { + setTagDrawerMode("manage"); + setIsTagDrawerOpen(true); + }, 150); + } + }, [loadSettings, loadTodos, loadTags, loadNotes]); + + // Global Keyboard shortcuts + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + // Esc closes drawers first, then hides window + if (e.key === "Escape") { + if (isSettingsOpen) { + setIsSettingsOpen(false); + return; + } + if (isTagDrawerOpen) { + setIsTagDrawerOpen(false); + return; + } + if (isTodoEditorOpen) { + setIsTodoEditorOpen(false); + return; + } + if (isNoteEditorOpen) { + setIsNoteEditorOpen(false); + return; + } + api.hideWindow(); + } + + // Cmd+1 -> Todos, Cmd+2 -> Notes + if ((e.metaKey || e.ctrlKey) && e.key === "1") { + e.preventDefault(); + setActiveTab("todos"); + } + if ((e.metaKey || e.ctrlKey) && e.key === "2") { + e.preventDefault(); + setActiveTab("notes"); + } + + // Cmd+, -> Settings + if ((e.metaKey || e.ctrlKey) && e.key === ",") { + e.preventDefault(); + setIsSettingsOpen(true); + } + + // Cmd+F -> Focus Search input + if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "f") { + e.preventDefault(); + const searchInput = document.querySelector('input[type="text"]') as HTMLInputElement | null; + searchInput?.focus(); + } + + // Cmd+N -> New Item + if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "n") { + e.preventDefault(); + if (isSettingsOpen || isTagDrawerOpen || isTodoEditorOpen || isNoteEditorOpen) return; + window.dispatchEvent(new CustomEvent("floatick:new-item")); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isSettingsOpen, isTagDrawerOpen, isTodoEditorOpen, isNoteEditorOpen]); + + const isScreenshotMode = + typeof window !== "undefined" && + window.location.search.includes("screenshot"); + + return ( +
+ {/* Refined Floatick Panel Surface */} +
+ {/* Panel Header */} +
setIsSettingsOpen(true)} + /> + + {/* Content Switcher Tabs */} + + + {/* Main Content Panels */} + {activeTab === "todos" ? ( + + ) : ( + + )} + + {/* Settings Drawer */} + setIsSettingsOpen(false)} + /> + + {/* Todo Editor Full Subview */} + setIsTodoEditorOpen(false)} + /> + + {/* Note Editor Full Subview */} + setIsNoteEditorOpen(false)} + /> + + {/* Tag Drawer */} + setIsTagDrawerOpen(false)} + /> +
+
+ ); +}; diff --git a/src/components/common/ActionBar.tsx b/src/components/common/ActionBar.tsx new file mode 100644 index 0000000..79b23da --- /dev/null +++ b/src/components/common/ActionBar.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { MagnifyingGlass, X, Tag, Plus } from "@phosphor-icons/react"; +import { useTranslation } from "react-i18next"; + +interface ActionBarProps { + query: string; + onQueryChange: (query: string) => void; + selectedTagCount?: number; + onOpenTagFilter: () => void; + onAddNew: () => void; + placeholder?: string; + addTooltip?: string; +} + +export const ActionBar: React.FC = ({ + query, + onQueryChange, + selectedTagCount = 0, + onOpenTagFilter, + onAddNew, + placeholder, + addTooltip, +}) => { + const { t } = useTranslation(); + + return ( +
+ {/* 42px Search Bar - Borderless & Clean */} +
+ onQueryChange(e.target.value)} + placeholder={placeholder || t("search")} + className="w-full h-full pl-9 pr-12 text-[13.5px] font-medium rounded-[8px] bg-[var(--color-bg-elevated)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-subtle)] focus:outline-none focus:bg-[var(--color-bg-elevated-hover)] focus:placeholder-transparent transition-colors" + /> + + + {/* Clear Button */} + {query && ( + + )} +
+ + {/* Tag Filter Button (42x42) - Clean Icon Button without selected background */} + + + {/* Add Button (42x42) - Clean Icon Button */} + +
+ ); +}; diff --git a/src/components/common/FloatickBrandMark.tsx b/src/components/common/FloatickBrandMark.tsx new file mode 100644 index 0000000..57d0dc3 --- /dev/null +++ b/src/components/common/FloatickBrandMark.tsx @@ -0,0 +1,54 @@ +import React from "react"; + +interface FloatickBrandMarkProps { + size?: number; + className?: string; +} + +export const FloatickBrandMark: React.FC = ({ + size = 38, + className = "", +}) => { + const borderRadius = Math.round(size * 0.31); + const strokeWidth = size * 0.07; + + return ( +
+ + {/* Back Checkmark */} + + {/* Front Checkmark */} + + +
+ ); +}; diff --git a/src/components/common/FloatickMarkdown.tsx b/src/components/common/FloatickMarkdown.tsx new file mode 100644 index 0000000..7dd4be5 --- /dev/null +++ b/src/components/common/FloatickMarkdown.tsx @@ -0,0 +1,137 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import Markdown from "react-markdown"; +import remarkGfm from "remark-gfm"; + +interface FloatickMarkdownProps { + content: string; + emptyMessage?: string; + className?: string; +} + +export const FloatickMarkdown: React.FC = ({ + content, + emptyMessage, + className = "", +}) => { + const { t } = useTranslation(); + const defaultEmpty = emptyMessage || t("markdownPreviewEmptyMessage"); + + if (!content || !content.trim()) { + return ( +
+ {defaultEmpty} +
+ ); + } + + return ( +
+ ( +

+ {children} +

+ ), + h2: ({ children }) => ( +

+ {children} +

+ ), + h3: ({ children }) => ( +

+ {children} +

+ ), + p: ({ children }) => ( +

+ {children} +

+ ), + ul: ({ children }) => ( +
    + {children} +
+ ), + ol: ({ children }) => ( +
    + {children} +
+ ), + li: ({ children }) => ( +
  • {children}
  • + ), + blockquote: ({ children }) => ( +
    + {children} +
    + ), + pre: ({ children }) => ( +
    +              {children}
    +            
    + ), + code: ({ className: codeClassName, children, ...props }) => { + const isCodeBlock = codeClassName && codeClassName.startsWith("language-"); + if (isCodeBlock) { + return {children}; + } + return ( + + {children} + + ); + }, + a: ({ href, children }) => ( + + {children} + + ), + hr: () =>
    , + table: ({ children }) => ( +
    + + {children} +
    +
    + ), + th: ({ children }) => ( + + {children} + + ), + td: ({ children }) => ( + + {children} + + ), + strong: ({ children }) => ( + + {children} + + ), + em: ({ children }) => ( + + {children} + + ), + del: ({ children }) => ( + {children} + ), + }} + > + {content} +
    +
    + ); +}; diff --git a/src/components/common/FloatickTiptapEditor.tsx b/src/components/common/FloatickTiptapEditor.tsx new file mode 100644 index 0000000..1350006 --- /dev/null +++ b/src/components/common/FloatickTiptapEditor.tsx @@ -0,0 +1,898 @@ +import React, { useEffect, useState, useRef, useMemo } from "react"; +import { useEditor, EditorContent, Editor } from "@tiptap/react"; +import StarterKit from "@tiptap/starter-kit"; +import TaskList from "@tiptap/extension-task-list"; +import TaskItem from "@tiptap/extension-task-item"; +import { Markdown } from "tiptap-markdown"; +import { + TextHOne, + TextHTwo, + TextHThree, + TextB, + TextItalic, + TextStrikethrough, + Code as InlineCodeIcon, + ListChecks, + ListBullets, + ListNumbers, + Quotes, + CodeBlock as CodeBlockIconPh, + Minus, + ArrowUUpLeft, + ArrowUUpRight, + DotsThree, + Eraser, +} from "@phosphor-icons/react"; + +// Refined, minimalist, pixel-crisp SVG icons for Slash Menu +const TaskIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + +); + +const BulletListIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + + + + + +); + +const NumberedListIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + + + + +); + +const HeadingOneIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + +); + +const HeadingTwoIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + +); + +const BlockquoteIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + +); + +const CodeBlockIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + + +); + +const DividerIcon: React.FC<{ className?: string }> = ({ className = "w-4 h-4" }) => ( + + + + + +); + +interface SlashCommandItem { + id: string; + label: string; + desc: string; + shortcut: string; + icon: React.FC<{ className?: string }>; + action: (editor: Editor) => void; +} + +const SLASH_COMMANDS: SlashCommandItem[] = [ + { + id: "task", + label: "任务清单", + desc: "交互式待办复选框", + shortcut: "/task", + icon: TaskIcon, + action: (editor) => editor.chain().focus().toggleTaskList().run(), + }, + { + id: "bullet", + label: "无序列表", + desc: "圆点项目列表", + shortcut: "/bullet", + icon: BulletListIcon, + action: (editor) => editor.chain().focus().toggleBulletList().run(), + }, + { + id: "ordered", + label: "有序列表", + desc: "数字编号列表", + shortcut: "/number", + icon: NumberedListIcon, + action: (editor) => editor.chain().focus().toggleOrderedList().run(), + }, + { + id: "h1", + label: "大标题", + desc: "一级主标题", + shortcut: "/h1", + icon: HeadingOneIcon, + action: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run(), + }, + { + id: "h2", + label: "中标题", + desc: "二级分段标题", + shortcut: "/h2", + icon: HeadingTwoIcon, + action: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run(), + }, + { + id: "quote", + label: "引用段落", + desc: "强调重点引文", + shortcut: "/quote", + icon: BlockquoteIcon, + action: (editor) => editor.chain().focus().toggleBlockquote().run(), + }, + { + id: "code", + label: "代码块", + desc: "等宽排版代码块", + shortcut: "/code", + icon: CodeBlockIcon, + action: (editor) => editor.chain().focus().toggleCodeBlock().run(), + }, + { + id: "divider", + label: "分割线", + desc: "水平内容分割线", + shortcut: "/divider", + icon: DividerIcon, + action: (editor) => editor.chain().focus().setHorizontalRule().run(), + }, +]; + +interface ToolbarButtonProps { + onClick: () => void; + isActive?: boolean; + disabled?: boolean; + label: string; + shortcut?: string; + suppressTooltip?: boolean; + children: React.ReactNode; +} + +const ToolbarButton: React.FC = ({ + onClick, + isActive = false, + disabled = false, + label, + shortcut, + suppressTooltip = false, + children, +}) => { + const [isHovered, setIsHovered] = useState(false); + const timerRef = useRef | null>(null); + + const handleMouseEnter = () => { + if (timerRef.current) clearTimeout(timerRef.current); + timerRef.current = setTimeout(() => { + setIsHovered(true); + }, 180); + }; + + const handleMouseLeave = () => { + if (timerRef.current) clearTimeout(timerRef.current); + setIsHovered(false); + }; + + return ( +
    + + + {/* Exquisite macOS / Raycast Floating Micro-Tooltip */} + {isHovered && !disabled && !suppressTooltip && ( +
    + {label} + {shortcut && ( + + {shortcut} + + )} +
    + )} +
    + ); +}; + +const ToolbarDivider: React.FC = () => ( +
    +); + +export const EditorToolbar: React.FC<{ editor: Editor | null }> = ({ editor }) => { + const [showMoreMenu, setShowMoreMenu] = useState(false); + + if (!editor) return null; + + const isMoreActive = + editor.isActive("blockquote") || + editor.isActive("codeBlock") || + editor.isActive("heading", { level: 3 }); + + return ( +
    + {/* Primary Formatting Tools */} +
    + {/* Headings */} + editor.chain().focus().toggleHeading({ level: 1 }).run()} + > + + + editor.chain().focus().toggleHeading({ level: 2 }).run()} + > + + + + + + {/* Inline styles */} + editor.chain().focus().toggleBold().run()} + > + + + editor.chain().focus().toggleItalic().run()} + > + + + editor.chain().focus().toggleStrike().run()} + > + + + editor.chain().focus().toggleCode().run()} + > + + + + + + {/* Lists */} + editor.chain().focus().toggleTaskList().run()} + > + + + editor.chain().focus().toggleBulletList().run()} + > + + + editor.chain().focus().toggleOrderedList().run()} + > + + + + + + {/* More Tools Dropdown Trigger */} +
    + setShowMoreMenu((prev) => !prev)} + > + + + + {/* More Menu Dropdown Popover */} + {showMoreMenu && ( + <> +
    { + e.stopPropagation(); + setShowMoreMenu(false); + }} + /> +
    e.stopPropagation()} + > +
    + 更多格式 +
    + + + + + + + + + +
    + + +
    + + )} +
    +
    + + {/* History (Undo / Redo) docked on the right */} +
    + editor.chain().focus().undo().run()} + > + + + editor.chain().focus().redo().run()} + > + + +
    +
    + ); +}; + +export interface FloatickEditorHandle { + getMarkdown: () => string; + setMarkdown: (content: string) => void; + focus: () => void; +} + +export interface FloatickTiptapEditorProps { + initialContent: string; + onChange?: (markdown: string) => void; + onCmdEnter?: () => void; + editorRef?: React.MutableRefObject; + className?: string; + showToolbar?: boolean; +} + +export const FloatickTiptapEditor: React.FC = ({ + initialContent, + onChange, + onCmdEnter, + editorRef, + className = "", + showToolbar = true, +}) => { + const containerRef = useRef(null); + const menuListRef = useRef(null); + const itemRefs = useRef<(HTMLButtonElement | null)[]>([]); + + const [, setSelectionTick] = useState(0); + + const [showSlashMenu, setShowSlashMenu] = useState(false); + const [slashQuery, setSlashQuery] = useState(""); + const [slashMenuIndex, setSlashMenuIndex] = useState(0); + const [menuCoords, setMenuCoords] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); + + // Filter commands based on slashQuery + const filteredCommands = useMemo(() => { + if (!slashQuery) return SLASH_COMMANDS; + const q = slashQuery.toLowerCase(); + return SLASH_COMMANDS.filter( + (cmd) => + cmd.label.toLowerCase().includes(q) || + cmd.id.toLowerCase().includes(q) || + cmd.shortcut.toLowerCase().includes(q) || + cmd.desc.toLowerCase().includes(q) + ); + }, [slashQuery]); + + // Keep refs synchronized with state to avoid stale closure issues in handleKeyDown + const showSlashMenuRef = useRef(showSlashMenu); + const slashMenuIndexRef = useRef(slashMenuIndex); + const filteredCommandsRef = useRef(filteredCommands); + const slashQueryRef = useRef(slashQuery); + + useEffect(() => { + showSlashMenuRef.current = showSlashMenu; + slashMenuIndexRef.current = slashMenuIndex; + filteredCommandsRef.current = filteredCommands; + slashQueryRef.current = slashQuery; + }, [showSlashMenu, slashMenuIndex, filteredCommands, slashQuery]); + + // Execute Slash Command + const handleSelectCommand = (cmd: SlashCommandItem) => { + if (!editor) return; + const { from } = editor.state.selection; + // Delete the '/' character and any query text typed after it + const deleteLength = 1 + slashQueryRef.current.length; + editor + .chain() + .focus() + .deleteRange({ from: Math.max(0, from - deleteLength), to: from }) + .run(); + cmd.action(editor); + setShowSlashMenu(false); + setSlashQuery(""); + }; + + const editor = useEditor({ + extensions: [ + StarterKit.configure({ + heading: { + levels: [1, 2, 3], + }, + }), + TaskList, + TaskItem.configure({ + nested: true, + }), + Markdown.configure({ + html: true, + transformPastedText: true, + transformCopiedText: true, + }), + ], + content: initialContent || "", + editorProps: { + attributes: { + class: "tiptap focus:outline-none min-h-[160px] pb-12", + }, + handleKeyDown: (_view, event) => { + if (event.isComposing || (event as any).keyCode === 229) { + return false; + } + + // If slash menu is active, intercept navigation keys before ProseMirror + if (showSlashMenuRef.current) { + const list = filteredCommandsRef.current; + if (list.length > 0) { + if (event.key === "ArrowDown") { + event.preventDefault(); + setSlashMenuIndex((prev) => (prev + 1) % list.length); + return true; + } + + if (event.key === "ArrowUp") { + event.preventDefault(); + setSlashMenuIndex((prev) => (prev - 1 + list.length) % list.length); + return true; + } + + if (event.key === "Enter" || event.key === "Tab") { + event.preventDefault(); + const selectedCmd = list[slashMenuIndexRef.current] || list[0]; + if (selectedCmd) { + handleSelectCommand(selectedCmd); + } + return true; + } + + if (event.key === "Escape") { + event.preventDefault(); + setShowSlashMenu(false); + setSlashQuery(""); + return true; + } + } + } + + // Cmd+Enter or Ctrl+Enter to trigger save + if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { + event.preventDefault(); + onCmdEnter?.(); + return true; + } + + return false; + }, + }, + onUpdate: ({ editor }) => { + const { from } = editor.state.selection; + const textBefore = editor.state.doc.textBetween(Math.max(0, from - 24), from, "\n"); + + // Match '/' preceded by start of block or whitespace, followed by optional alphanumeric/Chinese query + const slashMatch = textBefore.match(/(?:^|\s)\/([a-zA-Z0-9\u4e00-\u9fa5]*)$/); + + if (slashMatch) { + const query = slashMatch[1]; + setSlashQuery(query); + + // Calculate menu coordinate position + const coords = editor.view.coordsAtPos(from); + const containerRect = containerRef.current?.getBoundingClientRect(); + + if (containerRect) { + const menuHeight = 280; + const spaceBelow = containerRect.bottom - coords.bottom; + let relativeTop = coords.bottom - containerRect.top + 6; + + // If insufficient space below, flip above cursor + if (spaceBelow < menuHeight && coords.top - containerRect.top > menuHeight) { + relativeTop = coords.top - containerRect.top - menuHeight - 6; + } + + const relativeLeft = Math.min( + Math.max(8, coords.left - containerRect.left), + Math.max(8, containerRect.width - 272) + ); + setMenuCoords({ top: relativeTop, left: relativeLeft }); + } + + setShowSlashMenu(true); + setSlashMenuIndex(0); + } else { + if (showSlashMenu) { + setShowSlashMenu(false); + setSlashQuery(""); + } + } + + const md = (editor.storage as any).markdown?.getMarkdown?.() ?? ""; + onChange?.(md); + }, + onSelectionUpdate: () => { + setSelectionTick((t) => t + 1); + }, + onTransaction: () => { + setSelectionTick((t) => t + 1); + }, + }); + + // Auto-scroll selected item into view whenever slashMenuIndex changes + useEffect(() => { + if (showSlashMenu) { + const targetButton = itemRefs.current[slashMenuIndex]; + if (targetButton) { + targetButton.scrollIntoView({ + block: "nearest", + inline: "nearest", + behavior: "auto", + }); + } + } + }, [showSlashMenu, slashMenuIndex]); + + useEffect(() => { + if (editor && !editor.isDestroyed && initialContent && editor.isEmpty) { + editor.commands.setContent(initialContent); + } + }, [editor, initialContent]); + + // Reset scroll to top whenever slash menu first appears + useEffect(() => { + if (showSlashMenu) { + setSlashMenuIndex(0); + menuListRef.current?.scrollTo({ top: 0, behavior: "auto" }); + } + }, [showSlashMenu]); + + useEffect(() => { + if (typeof window !== "undefined" && window.location.search.includes("slash")) { + setTimeout(() => { + setMenuCoords({ top: 125, left: 16 }); + setShowSlashMenu(true); + }, 400); + } + }, []); + + // Expose methods via editorRef + useEffect(() => { + if (!editorRef) return; + editorRef.current = { + getMarkdown: () => { + if (!editor) return ""; + return (editor.storage as any).markdown?.getMarkdown?.() ?? ""; + }, + setMarkdown: (content: string) => { + if (!editor) return; + editor.commands.setContent(content || ""); + }, + focus: () => { + editor?.commands.focus(); + }, + }; + }, [editor, editorRef]); + + return ( +
    + {showToolbar && } + + + {/* Floating Slash Command Menu */} + {showSlashMenu && filteredCommands.length > 0 && ( + <> +
    { + setShowSlashMenu(false); + setSlashQuery(""); + }} + /> +
    +
    + 常用模块 +
    + {filteredCommands.map((cmd, index) => { + const Icon = cmd.icon; + const isSelected = index === slashMenuIndex; + return ( + + ); + })} +
    + + )} +
    + ); +}; + diff --git a/src/components/layout/ContentSwitcher.tsx b/src/components/layout/ContentSwitcher.tsx new file mode 100644 index 0000000..47fcc73 --- /dev/null +++ b/src/components/layout/ContentSwitcher.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +interface ContentSwitcherProps { + selected: "todos" | "notes"; + onSelected: (tab: "todos" | "notes") => void; +} + +export const ContentSwitcher: React.FC = ({ + selected, + onSelected, +}) => { + const { t } = useTranslation(); + + return ( +
    +
    + {/* Todos Tab Button */} + + + {/* Notes Tab Button */} + +
    +
    + ); +}; diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx new file mode 100644 index 0000000..9ade17b --- /dev/null +++ b/src/components/layout/Header.tsx @@ -0,0 +1,101 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Archive, GearSix, CaretUp } from "@phosphor-icons/react"; +import { FloatickBrandMark } from "@/components/common/FloatickBrandMark"; +import { useTodoStore } from "@/stores/useTodoStore"; +import { useNoteStore } from "@/stores/useNoteStore"; +import { api } from "@/lib/api"; + +interface HeaderProps { + activeTab: "todos" | "notes"; + onOpenSettings: () => void; +} + +export const Header: React.FC = ({ + activeTab, + onOpenSettings, +}) => { + const { t } = useTranslation(); + + const todos = useTodoStore((s) => s.todos); + const activeScope = useTodoStore((s) => s.activeScope); + const setActiveScope = useTodoStore((s) => s.setActiveScope); + + const notes = useNoteStore((s) => s.notes); + + const activeTodoCount = todos.filter((t) => !t.completedAt && !t.archivedAt).length; + const archivedTodoCount = todos.filter((t) => !!t.archivedAt).length; + + const activeNoteCount = notes.filter((n) => !n.archivedAt).length; + const archivedNoteCount = notes.filter((n) => !!n.archivedAt).length; + + const isArchived = activeScope === "archived"; + + const statusText = (() => { + if (activeTab === "notes") { + return isArchived + ? `${t("archive")} · ${archivedNoteCount}` + : t("noteCount", { count: activeNoteCount }); + } + if (isArchived) { + return `${t("archive")} · ${archivedTodoCount}`; + } + return activeTodoCount === 0 + ? t("allClear") + : t("tasksRemaining", { count: activeTodoCount }); + })(); + + const handleToggleArchive = () => { + setActiveScope(isArchived ? "active" : "archived"); + }; + + const handleCollapse = async () => { + await api.hideWindow(); + }; + + return ( +
    + {/* Left: 38px Brand Mark + Status Text */} +
    + + + {statusText} + +
    + + {/* Right: Icon Buttons */} +
    + {/* Archive */} + + + {/* Settings */} + + + {/* Collapse */} + +
    +
    + ); +}; diff --git a/src/components/notes/NoteEditorDrawer.tsx b/src/components/notes/NoteEditorDrawer.tsx new file mode 100644 index 0000000..07176cc --- /dev/null +++ b/src/components/notes/NoteEditorDrawer.tsx @@ -0,0 +1,504 @@ +import React, { useState, useEffect, useRef } from "react"; +import { useTranslation } from "react-i18next"; +import { + X, + PushPin, + Archive, + Trash, + Check, + Tag, + Eye, + PencilSimple, +} from "@phosphor-icons/react"; +import { useNoteStore } from "@/stores/useNoteStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { FloatickTiptapEditor, FloatickEditorHandle } from "@/components/common/FloatickTiptapEditor"; +import { FloatickMarkdown } from "@/components/common/FloatickMarkdown"; + +interface NoteEditorDrawerProps { + noteId: string | null; + isOpen: boolean; + onClose: () => void; +} + +export const NoteEditorDrawer: React.FC = ({ + noteId, + isOpen, + onClose, +}) => { + const { t } = useTranslation(); + const notes = useNoteStore((s) => s.notes); + const addNote = useNoteStore((s) => s.addNote); + const updateNote = useNoteStore((s) => s.updateNote); + const deleteNote = useNoteStore((s) => s.deleteNote); + const togglePin = useNoteStore((s) => s.togglePin); + const toggleArchive = useNoteStore((s) => s.toggleArchive); + const storeEditorMode = useNoteStore((s) => s.editorMode); + + const tagsWorkspace = useTagStore((s) => s.workspace); + + const note = notes.find((n) => n.id === noteId); + const isCreate = !note; + + const [mode, setMode] = useState<"view" | "edit">("view"); + const [title, setTitle] = useState(""); + const [content, setContent] = useState(""); + const [selectedTagIds, setSelectedTagIds] = useState([]); + const [showTagMenu, setShowTagMenu] = useState(false); + + const titleInputRef = useRef(null); + const editorHandleRef = useRef(null); + const isSavingRef = useRef(false); + + const existingNoteId = note?.id; + + useEffect(() => { + if (isOpen) { + if (note) { + setTitle(note.title); + setContent(note.content || ""); + setSelectedTagIds(note.tagIds || []); + setMode(storeEditorMode || "view"); + } else { + setTitle(""); + setContent(""); + setSelectedTagIds([]); + setMode("edit"); + } + setShowTagMenu(false); + + if (!note) { + setTimeout(() => { + titleInputRef.current?.focus(); + }, 60); + } + } + }, [existingNoteId, isOpen, storeEditorMode]); + + // Global keyboard shortcuts + useEffect(() => { + if (!isOpen) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.isComposing || (e as any).keyCode === 229) return; + + // In view mode: pressing 'e' or 'E' (not in an input) switches to edit + if (mode === "view") { + const activeTag = document.activeElement?.tagName?.toLowerCase(); + if (activeTag !== "input" && activeTag !== "textarea") { + if (e.key.toLowerCase() === "e") { + e.preventDefault(); + setMode("edit"); + return; + } + } + } + + // In edit mode: Cmd+Enter to save + if (mode === "edit" && (e.metaKey || e.ctrlKey) && e.key === "Enter") { + e.preventDefault(); + handleSave(); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isOpen, mode, title, content, selectedTagIds]); + + if (!isOpen) return null; + + const handleSave = async () => { + if (isSavingRef.current) return; + const trimmedTitle = title.trim(); + const currentMarkdown = editorHandleRef.current?.getMarkdown() ?? content; + const trimmedContent = currentMarkdown.trim(); + + if (!trimmedTitle && !trimmedContent) { + onClose(); + return; + } + + const finalTitle = trimmedTitle || t("untitledNote"); + + isSavingRef.current = true; + try { + if (isCreate) { + await addNote(finalTitle, trimmedContent, selectedTagIds); + } else { + await updateNote(note.id, { + title: finalTitle, + content: trimmedContent, + tagIds: selectedTagIds, + }); + } + onClose(); + } finally { + isSavingRef.current = false; + } + }; + + const toggleTag = (tagId: string) => { + if (selectedTagIds.includes(tagId)) { + setSelectedTagIds(selectedTagIds.filter((id) => id !== tagId)); + } else { + setSelectedTagIds([...selectedTagIds, tagId]); + } + }; + + const canSave = title.trim().length > 0 || content.trim().length > 0; + + return ( +
    + {/* Top Header Bar */} +
    +
    + + {isCreate + ? t("newNoteDrawerTitle") + : mode === "view" + ? t("noteDetailsDrawerTitle") + : t("editNoteDrawerTitle")} + + + {/* View / Edit Mode Switcher (only for existing notes) */} + {!isCreate && ( +
    + + +
    + )} + + {note && ( +
    + + + +
    + )} +
    + + +
    + + {/* Main Content Area */} + {mode === "view" ? ( + /* ==================== VIEW MODE ==================== */ +
    + {/* Title as Crisp Typography Heading */} +
    setMode("edit")} + className="px-5 pt-4 pb-2 shrink-0 select-text cursor-text" + title={t("doubleClickToEdit")} + > +

    + {title || t("untitledNote")} +

    +
    + + {/* Properties: Tags */} + {selectedTagIds.length > 0 && ( +
    + {selectedTagIds.map((tagId) => { + const tag = tagsWorkspace.tags.find((t) => t.id === tagId); + if (!tag) return null; + return ( + + + {tag.name} + + ); + })} +
    + )} + + {/* Clean Hairline Divider */} +
    + + {/* View Mode Markdown Content */} +
    setMode("edit")} + className="flex-1 flex flex-col min-h-0 px-5 pt-3 pb-2 overflow-y-auto smooth-scroll select-text" + > + {content.trim() ? ( + + ) : ( +
    setMode("edit")} + className="h-full min-h-[140px] flex flex-col items-center justify-center text-[var(--color-text-subtle)] text-xs cursor-pointer hover:text-[var(--color-teal-primary)] transition-colors select-none" + > + {t("clickToAddContent")} +
    + )} +
    + + {/* View Mode Footer */} +
    + + {t("viewModeHint")} + + +
    + + +
    +
    +
    + ) : ( + /* ==================== EDIT MODE ==================== */ +
    + {/* Title Input (No blue text selection!) */} +
    + { + const val = e.target.value; + if (!title && val === " ") return; + setTitle(val); + }} + onKeyDown={(e) => { + if (e.nativeEvent.isComposing || e.keyCode === 229) return; + if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { + e.preventDefault(); + e.stopPropagation(); + handleSave(); + return; + } + if (e.key === "Enter") { + e.preventDefault(); + editorHandleRef.current?.focus(); + } + }} + placeholder={t("noteTitlePlaceholder")} + className="w-full text-[17px] font-semibold bg-transparent text-[var(--color-text-primary)] placeholder:text-[var(--color-text-subtle)] focus:outline-none focus:placeholder-transparent transition-colors tracking-tight" + /> +
    + + {/* Metadata Properties Row: Tags directly beneath Title */} +
    + {/* Tag Selector Trigger Button */} +
    + + + {/* Tag Dropdown Popover */} + {showTagMenu && ( + <> +
    setShowTagMenu(false)} + /> +
    +
    + {t("tags")} +
    + {tagsWorkspace.tags.length === 0 ? ( +
    + {t("noTagsYetMessage")} +
    + ) : ( + tagsWorkspace.tags.map((tag) => { + const isSelected = selectedTagIds.includes(tag.id); + return ( + + ); + }) + )} +
    + + )} +
    + + {/* Selected Tag Pills */} + {selectedTagIds.map((tagId) => { + const tag = tagsWorkspace.tags.find((t) => t.id === tagId); + if (!tag) return null; + return ( + + ); + })} +
    + + {/* Clean Hairline Divider */} +
    + + {/* Tiptap Editor Canvas */} +
    + setContent(md)} + onCmdEnter={handleSave} + /> +
    + + {/* Edit Mode Bottom Footer Bar */} +
    + + {t("shortcutHint")} + + +
    + + +
    +
    +
    + )} +
    + ); +}; diff --git a/src/components/notes/NoteItemRow.tsx b/src/components/notes/NoteItemRow.tsx new file mode 100644 index 0000000..37e2733 --- /dev/null +++ b/src/components/notes/NoteItemRow.tsx @@ -0,0 +1,122 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { PushPin, Archive, Trash, PencilSimple } from "@phosphor-icons/react"; +import type { NoteItem } from "@/types"; +import { useNoteStore } from "@/stores/useNoteStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { formatTime } from "@/lib/dateUtils"; + +interface NoteItemRowProps { + note: NoteItem; + onView: (note: NoteItem) => void; + onEdit: (note: NoteItem) => void; +} + +export const NoteItemRow: React.FC = ({ note, onView, onEdit }) => { + const { t } = useTranslation(); + const togglePin = useNoteStore((s) => s.togglePin); + const toggleArchive = useNoteStore((s) => s.toggleArchive); + const deleteNote = useNoteStore((s) => s.deleteNote); + + const tagsWorkspace = useTagStore((s) => s.workspace); + const tags = tagsWorkspace.tags.filter((t) => note.tagIds.includes(t.id)); + + const isPinned = !!note.pinnedAt; + const isArchived = !!note.archivedAt; + + return ( +
    onView(note)} + className={`group relative p-2.5 rounded-[8px] transition-colors cursor-pointer select-none ${ + isPinned + ? "bg-[var(--color-teal-tint)] hover:bg-[var(--color-teal-tint-active)]" + : "hover:bg-[var(--color-row-hover)]" + }`} + > + {/* Top row: Title (Unobstructed full width) */} +
    +

    + {isPinned && } + {note.title || t("newNote")} +

    +
    + + {/* Snippet */} + {note.content && ( +

    + {note.content} +

    + )} + + {/* Bottom meta & Floating Actions */} +
    +
    + {tags.slice(0, 3).map((tag) => ( + + + {tag.name} + + ))} + {tags.length > 3 && +{tags.length - 3}} +
    + + {formatTime(note.updatedAt)} + + + {/* Floating Hover action icons with Frosted Blur Backdrop on bottom line */} +
    e.stopPropagation()} + > +
    + + + + +
    +
    +
    +
    + ); +}; diff --git a/src/components/notes/NotePanel.tsx b/src/components/notes/NotePanel.tsx new file mode 100644 index 0000000..9560648 --- /dev/null +++ b/src/components/notes/NotePanel.tsx @@ -0,0 +1,171 @@ +import React, { useMemo, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { Note, PushPin } from "@phosphor-icons/react"; +import type { NoteItem } from "@/types"; +import { useNoteStore } from "@/stores/useNoteStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { ActionBar } from "@/components/common/ActionBar"; +import { NoteItemRow } from "./NoteItemRow"; +import { getGroupLabel } from "@/lib/dateUtils"; + +interface NotePanelProps { + onOpenTagFilter: () => void; +} + +export const NotePanel: React.FC = ({ onOpenTagFilter }) => { + const { t, i18n } = useTranslation(); + + const notes = useNoteStore((s) => s.notes); + const searchQuery = useNoteStore((s) => s.searchQuery); + const setSearchQuery = useNoteStore((s) => s.setSearchQuery); + + const selectedTagIds = useTagStore((s) => s.selectedTagIds); + + const setIsEditorOpen = useNoteStore((s) => s.setIsEditorOpen); + const setEditingNoteId = useNoteStore((s) => s.setEditingNoteId); + const setEditorMode = useNoteStore((s) => s.setEditorMode); + + const handleOpenCreate = () => { + setEditingNoteId(null); + setEditorMode("edit"); + setIsEditorOpen(true); + }; + + useEffect(() => { + const handler = () => handleOpenCreate(); + window.addEventListener("floatick:new-item", handler); + return () => window.removeEventListener("floatick:new-item", handler); + }, []); + + const handleOpenView = (note: NoteItem) => { + setEditingNoteId(note.id); + setEditorMode("view"); + setIsEditorOpen(true); + }; + + const handleOpenEdit = (note: NoteItem) => { + setEditingNoteId(note.id); + setEditorMode("edit"); + setIsEditorOpen(true); + }; + + // Filter notes + const filteredNotes = useMemo(() => { + return notes.filter((item) => { + if (item.archivedAt) return false; + + if (selectedTagIds.length > 0) { + const matchesTag = selectedTagIds.some((id) => item.tagIds.includes(id)); + if (!matchesTag) return false; + } + + if (searchQuery.trim()) { + const q = searchQuery.toLowerCase(); + const matchTitle = item.title.toLowerCase().includes(q); + const matchContent = item.content.toLowerCase().includes(q); + if (!matchTitle && !matchContent) return false; + } + + return true; + }); + }, [notes, selectedTagIds, searchQuery]); + + // Separate pinned and unpinned + const pinnedNotes = useMemo(() => { + return filteredNotes.filter((n) => !!n.pinnedAt); + }, [filteredNotes]); + + const regularNotes = useMemo(() => { + return filteredNotes.filter((n) => !n.pinnedAt); + }, [filteredNotes]); + + // Group regular notes by date + const groupedNotes = useMemo(() => { + const groups: { label: string; items: NoteItem[] }[] = []; + const map = new Map(); + + for (const item of regularNotes) { + const label = getGroupLabel(item.updatedAt, i18n.language); + if (!map.has(label)) { + map.set(label, []); + } + map.get(label)!.push(item); + } + + for (const [label, items] of map.entries()) { + groups.push({ label, items }); + } + + return groups; + }, [regularNotes, i18n.language]); + + return ( +
    + {/* Action Bar */} + + + {/* Notes List */} +
    + {filteredNotes.length === 0 ? ( +
    + +

    + {t("noNotes")} +

    +

    + {t("noNotesSub")} +

    +
    + ) : ( + <> + {/* Pinned Section */} + {pinnedNotes.length > 0 && ( +
    +
    + + {t("pinnedNotes")} +
    +
    + {pinnedNotes.map((note) => ( + + ))} +
    +
    + )} + + {/* Date Grouped Regular Notes */} + {groupedNotes.map((group) => ( +
    +
    + {group.label} +
    +
    + {group.items.map((note) => ( + + ))} +
    +
    + ))} + + )} +
    +
    + ); +}; diff --git a/src/components/settings/SettingsDrawer.tsx b/src/components/settings/SettingsDrawer.tsx new file mode 100644 index 0000000..cc14d48 --- /dev/null +++ b/src/components/settings/SettingsDrawer.tsx @@ -0,0 +1,265 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + X, + Sun, + Moon, + Desktop, + Copy, + Check, + SignOut, +} from "@phosphor-icons/react"; +import { useSettingsStore } from "@/stores/useSettingsStore"; +import { api } from "@/lib/api"; +import type { ThemePreference, LanguagePreference } from "@/types"; + +interface SettingsDrawerProps { + isOpen: boolean; + onClose: () => void; +} + +const ToggleSwitch: React.FC<{ + checked: boolean; + onChange: (checked: boolean) => void; +}> = ({ checked, onChange }) => ( + +); + +export const SettingsDrawer: React.FC = ({ + isOpen, + onClose, +}) => { + const { t } = useTranslation(); + const settings = useSettingsStore((s) => s.settings); + const updateTheme = useSettingsStore((s) => s.updateTheme); + const updateLanguage = useSettingsStore((s) => s.updateLanguage); + const updateAlwaysOnTop = useSettingsStore((s) => s.updateAlwaysOnTop); + const updateCollapseOnBlur = useSettingsStore((s) => s.updateCollapseOnBlur); + + const [autostart, setAutostart] = useState(false); + const [copiedPath, setCopiedPath] = useState(false); + + useEffect(() => { + if (isOpen) { + api.isAutostartEnabled().then(setAutostart).catch(() => {}); + } + }, [isOpen]); + + if (!isOpen) return null; + + const handleToggleAutostart = async () => { + const next = !autostart; + try { + await api.setAutostartEnabled(next); + setAutostart(next); + } catch (err) { + console.error("Failed to update autostart:", err); + } + }; + + const handleCopyPath = () => { + navigator.clipboard.writeText("~/.floatick"); + setCopiedPath(true); + setTimeout(() => setCopiedPath(false), 1800); + }; + + const handleQuit = async () => { + await api.quitApp(); + }; + + return ( +
    + {/* Header */} +
    + + {t("settingsTitle")} + + +
    + + {/* Settings Body */} +
    + {/* Appearance & Language */} +
    + + {t("appearanceSectionTitle")} + +
    + {/* Theme */} +
    + + {t("themeLabel")} + +
    + {[ + { id: "system" as ThemePreference, label: t("themeSystemTooltip"), icon: Desktop }, + { id: "light" as ThemePreference, label: t("themeLightTooltip"), icon: Sun }, + { id: "dark" as ThemePreference, label: t("themeDarkTooltip"), icon: Moon }, + ].map(({ id, label, icon: Icon }) => { + const isActive = settings.theme === id; + return ( + + ); + })} +
    +
    + + {/* Language */} +
    + + {t("languageLabel")} + +
    + {[ + { id: "zh" as LanguagePreference, label: "中文" }, + { id: "en" as LanguagePreference, label: "English" }, + { id: "system" as LanguagePreference, label: "Auto" }, + ].map(({ id, label }) => { + const isActive = settings.language === id; + return ( + + ); + })} +
    +
    +
    +
    + + {/* Preferences */} +
    + + {t("preferencesSectionTitle")} + +
    + {/* Launch at Login */} +
    + + {t("openAtLoginLabel")} + + +
    + + {/* Always on Top */} +
    + + {t("alwaysOnTopLabel")} + + +
    + + {/* Collapse on Blur */} +
    + + {t("collapseWhenClickingOutsideLabel")} + + +
    +
    +
    + + {/* About & Data */} +
    + + {t("aboutSectionTitle")} + +
    + {/* Storage Path */} +
    + + {t("workingDirectorySectionTitle")} + + +
    + + {/* Version */} +
    + + {t("versionLabel")} + + + v0.4.0 + +
    +
    +
    + + {/* Quit Action */} +
    + +
    +
    +
    + ); +}; diff --git a/src/components/tags/TagDrawer.tsx b/src/components/tags/TagDrawer.tsx new file mode 100644 index 0000000..141e9df --- /dev/null +++ b/src/components/tags/TagDrawer.tsx @@ -0,0 +1,505 @@ +import React, { useState, useEffect, useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { + X, + Check, + CaretLeft, + Stack, + PencilSimple, + Trash, + Plus, +} from "@phosphor-icons/react"; +import { useTagStore } from "@/stores/useTagStore"; +import { useTodoStore } from "@/stores/useTodoStore"; +import { useNoteStore } from "@/stores/useNoteStore"; + +// Exact TagPalette colors from legacy/flutter/lib/features/todos/presentation/widgets/tag_palette.dart +const TAG_PALETTE = [ + "#20B8A8", // Teal + "#4C8FF5", // Blue + "#6F73E8", // Indigo + "#A46BE0", // Purple + "#E36F9F", // Pink + "#F18A45", // Orange + "#E0B83F", // Yellow + "#E15F5F", // Red +]; + +interface TagDrawerProps { + isOpen: boolean; + initialMode?: "filter" | "assignment" | "manage"; + targetTodoId?: string | null; + onClose: () => void; +} + +export const TagDrawer: React.FC = ({ + isOpen, + initialMode = "filter", + targetTodoId = null, + onClose, +}) => { + const { t } = useTranslation(); + + const workspace = useTagStore((s) => s.workspace); + const selectedTagIds = useTagStore((s) => s.selectedTagIds); + const toggleTagFilter = useTagStore((s) => s.toggleTagFilter); + const clearTagFilter = useTagStore((s) => s.clearTagFilter); + const toggleTodoTag = useTagStore((s) => s.toggleTodoTag); + const createTag = useTagStore((s) => s.createTag); + const updateTag = useTagStore((s) => s.updateTag); + const deleteTag = useTagStore((s) => s.deleteTag); + + const todos = useTodoStore((s) => s.todos); + const notes = useNoteStore((s) => s.notes); + + const [mode, setMode] = useState<"filter" | "assignment" | "manage">(initialMode); + const [returnMode, setReturnMode] = useState<"filter" | "assignment">("filter"); + + const [tagNameInput, setTagNameInput] = useState(""); + const [selectedColor, setSelectedColor] = useState(TAG_PALETTE[0]); + const [editingTagId, setEditingTagId] = useState(null); + const [confirmDeleteTagId, setConfirmDeleteTagId] = useState(null); + const [validationError, setValidationError] = useState(null); + + useEffect(() => { + if (isOpen) { + setMode(initialMode); + setReturnMode(initialMode === "manage" ? "filter" : initialMode); + setTagNameInput(""); + setSelectedColor(TAG_PALETTE[0]); + setEditingTagId(null); + setConfirmDeleteTagId(null); + setValidationError(null); + } + }, [isOpen, initialMode, targetTodoId]); + + // Compute accurate active usage counts (Active Todos + Active Notes) + const usageCounts = useMemo(() => { + const counts: Record = {}; + for (const tag of workspace.tags) { + counts[tag.id] = 0; + } + + // Active todos + for (const todo of todos) { + if (todo.archivedAt) continue; + const assigned = workspace.assignments[todo.id] || []; + for (const tagId of assigned) { + if (counts[tagId] !== undefined) { + counts[tagId]++; + } + } + } + + // Active notes + for (const note of notes) { + if (note.archivedAt) continue; + for (const tagId of note.tagIds) { + if (counts[tagId] !== undefined) { + counts[tagId]++; + } + } + } + + return counts; + }, [workspace.tags, workspace.assignments, todos, notes]); + + if (!isOpen) return null; + + const targetAssignedTagIds = targetTodoId + ? workspace.assignments[targetTodoId] || [] + : []; + + const handleOpenManage = () => { + setReturnMode(mode === "manage" ? "filter" : mode); + setMode("manage"); + handleCancelEdit(); + }; + + const handleBackFromManage = () => { + setMode(returnMode); + handleCancelEdit(); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + const name = tagNameInput.trim(); + if (!name) return; + + try { + if (editingTagId) { + await updateTag(editingTagId, name, selectedColor); + setEditingTagId(null); + } else { + await createTag(name, selectedColor); + } + setTagNameInput(""); + setSelectedColor(TAG_PALETTE[0]); + setValidationError(null); + } catch (err: unknown) { + if (err instanceof Error && err.message === "Tag name already exists") { + setValidationError(t("tagNameAlreadyExistsMessage") || "标签名称已存在"); + } else { + setValidationError(String(err)); + } + } + }; + + const handleStartEdit = (tag: { id: string; name: string; colorHex: string }) => { + setEditingTagId(tag.id); + setTagNameInput(tag.name); + setSelectedColor(tag.colorHex); + setConfirmDeleteTagId(null); + setValidationError(null); + }; + + const handleCancelEdit = () => { + setEditingTagId(null); + setTagNameInput(""); + setSelectedColor(TAG_PALETTE[0]); + setValidationError(null); + }; + + const handleDelete = async (tagId: string) => { + await deleteTag(tagId); + setConfirmDeleteTagId(null); + if (editingTagId === tagId) { + handleCancelEdit(); + } + }; + + return ( + <> + {/* Scrim Overlay */} +
    + + {/* Surface: 292px width, sliding from right */} +
    + {/* Header */} +
    +
    + {mode === "manage" && ( + + )} + + {mode === "filter" + ? t("filterByTagTitle") + : mode === "assignment" + ? t("assignTagsTitle") || "分配标签" + : t("manageTags")} + +
    + +
    + {mode !== "manage" && ( + + )} + +
    +
    + + {/* View 1: Filter Mode (TagFilterDrawer - Multi-Select Filter) */} + {mode === "filter" && ( +
    + {/* All Items Row (Clears multi-selection) */} + + + {/* Tags List */} + {workspace.tags.length === 0 ? ( +
    + {t("noTagsYetMessage")} +
    + ) : ( + workspace.tags.map((tag) => { + const isSelected = selectedTagIds.includes(tag.id); + const count = usageCounts[tag.id] ?? 0; + return ( + + ); + }) + )} +
    + )} + + {/* View 2: Assignment Mode (TagFilterDrawer.assignment - Assign to specific item) */} + {mode === "assignment" && targetTodoId && ( +
    + {workspace.tags.length === 0 ? ( +
    + {t("noTagsYetMessage")} +
    + ) : ( + workspace.tags.map((tag) => { + const isAssigned = targetAssignedTagIds.includes(tag.id); + const count = usageCounts[tag.id] ?? 0; + return ( + + ); + }) + )} +
    + )} + + {/* View 3: Management Mode (TagManagementDrawer) */} + {mode === "manage" && ( +
    + {/* Top Create / Edit Section */} +
    +
    + { + setTagNameInput(e.target.value); + if (validationError) setValidationError(null); + }} + placeholder={editingTagId ? t("tagName") : t("searchOrCreateTagHint") || "搜索或创建标签…"} + className="w-full h-9.5 pl-3 pr-16 rounded-[8px] bg-[var(--color-bg-elevated)] text-[13px] font-medium text-[var(--color-text-primary)] placeholder:text-[var(--color-text-subtle)] focus:outline-none focus:bg-[var(--color-bg-elevated-hover)] transition-colors" + /> + +
    + {editingTagId && ( + + )} + +
    +
    + + {/* Character limit and validation message */} +
    + + {validationError || (editingTagId ? "编辑标签模式" : "创建标签模式")} + + + {tagNameInput.length}/30 + +
    + + {/* TagPalette: 8 Circular Colors */} +
    + {TAG_PALETTE.map((color) => { + const isSelected = selectedColor === color; + return ( + + ); + })} +
    +
    + + {/* Managed Tags List */} +
    + {workspace.tags.length === 0 ? ( +
    + {t("noTagsYetMessage")} +
    + ) : ( + workspace.tags.map((tag) => { + const isEditing = editingTagId === tag.id; + const isConfirming = confirmDeleteTagId === tag.id; + const count = usageCounts[tag.id] ?? 0; + + return ( +
    + {/* Left: Tag Chip */} +
    + + {tag.name} +
    + + {/* Right: Usage count + Actions */} +
    + {!isConfirming && ( + + {count} + + )} + + {isConfirming ? ( +
    + + +
    + ) : ( +
    + + +
    + )} +
    +
    + ); + }) + )} +
    +
    + )} +
    + + ); +}; diff --git a/src/components/todos/TodoDeadlinePicker.tsx b/src/components/todos/TodoDeadlinePicker.tsx new file mode 100644 index 0000000..13714fd --- /dev/null +++ b/src/components/todos/TodoDeadlinePicker.tsx @@ -0,0 +1,236 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { + X, + CaretLeft, + CaretRight, + CalendarBlank, + Bell, + Check, + Trash, +} from "@phosphor-icons/react"; +import { + format, + addMonths, + subMonths, + startOfMonth, + endOfMonth, + startOfWeek, + endOfWeek, + eachDayOfInterval, + isSameMonth, + isSameDay, + isToday, + parseISO, +} from "date-fns"; +import { zhCN, enUS } from "date-fns/locale"; + +interface TodoDeadlinePickerProps { + initialDueAt?: string | null; + initialReminderAt?: string | null; + onSave: (dueAt: string | null, reminderAt: string | null) => void; + onClose: () => void; +} + +export const TodoDeadlinePicker: React.FC = ({ + initialDueAt, + initialReminderAt, + onSave, + onClose, +}) => { + const { t, i18n } = useTranslation(); + const dateLocale = i18n.language.startsWith("zh") ? zhCN : enUS; + + const initialDate = initialDueAt ? parseISO(initialDueAt) : new Date(); + + const [currentMonth, setCurrentMonth] = useState(initialDate); + const [selectedDay, setSelectedDay] = useState(initialDate); + const [timeStr, setTimeStr] = useState( + initialDueAt ? format(parseISO(initialDueAt), "HH:mm") : "18:00" + ); + const [reminderChoice, setReminderChoice] = useState( + initialReminderAt ? "custom" : "atDeadline" + ); + + const monthStart = startOfMonth(currentMonth); + const monthEnd = endOfMonth(monthStart); + const startDate = startOfWeek(monthStart, { weekStartsOn: 0 }); + const endDate = endOfWeek(monthEnd, { weekStartsOn: 0 }); + + const calendarDays = eachDayOfInterval({ start: startDate, end: endDate }); + + const handlePrevMonth = () => setCurrentMonth(subMonths(currentMonth, 1)); + const handleNextMonth = () => setCurrentMonth(addMonths(currentMonth, 1)); + + const handleSave = () => { + const [hours, minutes] = timeStr.split(":").map(Number); + const resultDate = new Date(selectedDay); + resultDate.setHours(hours || 0, minutes || 0, 0, 0); + + const dueIso = resultDate.toISOString(); + + let remIso: string | null = null; + if (reminderChoice === "atDeadline") { + remIso = dueIso; + } else if (reminderChoice === "10m") { + remIso = new Date(resultDate.getTime() - 10 * 60 * 1000).toISOString(); + } else if (reminderChoice === "1h") { + remIso = new Date(resultDate.getTime() - 60 * 60 * 1000).toISOString(); + } else if (reminderChoice === "1d") { + remIso = new Date(resultDate.getTime() - 24 * 60 * 60 * 1000).toISOString(); + } + + onSave(dueIso, remIso); + onClose(); + }; + + const handleClear = () => { + onSave(null, null); + onClose(); + }; + + const weekDayLabels = i18n.language.startsWith("zh") + ? ["日", "一", "二", "三", "四", "五", "六"] + : ["S", "M", "T", "W", "T", "F", "S"]; + + return ( +
    +
    + {/* Header */} +
    + + {t("setDeadline")} + + +
    + + {/* Month Navigation */} +
    + + {format(currentMonth, t("dateFormatMonthYear"), { locale: dateLocale })} + +
    + + +
    +
    + + {/* Weekday Row */} +
    + {weekDayLabels.map((d, i) => ( +
    + {d} +
    + ))} +
    + + {/* Days Grid */} +
    + {calendarDays.map((day) => { + const isSelected = isSameDay(day, selectedDay); + const isCurrentMonth = isSameMonth(day, currentMonth); + const isCurrentToday = isToday(day); + + return ( + + ); + })} +
    + + {/* Time & Deadline Input Section */} +
    +
    +
    + + + {format(selectedDay, t("dateFormatMonthDay"), { locale: dateLocale })} + +
    + setTimeStr(e.target.value)} + className="px-2 py-0.5 rounded-[6px] bg-[var(--color-bg-elevated)] text-xs font-mono text-[var(--color-text-primary)] border border-[var(--color-border-panel)] outline-none focus:border-[var(--color-teal-primary)]" + /> +
    + + {/* Reminder Selection */} +
    +
    + + {t("reminder")} +
    + +
    +
    + + {/* Footer Actions */} +
    + {initialDueAt ? ( + + ) : ( +
    + )} + + +
    +
    +
    + ); +}; diff --git a/src/components/todos/TodoEditorDrawer.tsx b/src/components/todos/TodoEditorDrawer.tsx new file mode 100644 index 0000000..801d7d4 --- /dev/null +++ b/src/components/todos/TodoEditorDrawer.tsx @@ -0,0 +1,545 @@ +import React, { useState, useEffect, useRef } from "react"; +import { useTranslation } from "react-i18next"; +import { + X, + Tag, + CalendarBlank, + Check, + Eye, + PencilSimple, + Clock, +} from "@phosphor-icons/react"; +import { useTodoStore } from "@/stores/useTodoStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { TodoDeadlinePicker } from "./TodoDeadlinePicker"; +import { FloatickTiptapEditor, FloatickEditorHandle } from "@/components/common/FloatickTiptapEditor"; +import { FloatickMarkdown } from "@/components/common/FloatickMarkdown"; +import { formatDeadline } from "@/lib/dateUtils"; + +interface TodoEditorDrawerProps { + todoId: string | null; + isOpen: boolean; + onClose: () => void; +} + +export const TodoEditorDrawer: React.FC = ({ + todoId, + isOpen, + onClose, +}) => { + const { t, i18n } = useTranslation(); + const todos = useTodoStore((s) => s.todos); + const addTodo = useTodoStore((s) => s.addTodo); + const updateTodo = useTodoStore((s) => s.updateTodo); + const storeEditorMode = useTodoStore((s) => s.editorMode); + + const tagsWorkspace = useTagStore((s) => s.workspace); + const setTodoTags = useTagStore((s) => s.setTodoTags); + + const existingTodo = todos.find((t) => t.id === todoId); + const isCreate = !existingTodo; + + const [mode, setMode] = useState<"view" | "edit">("view"); + const [title, setTitle] = useState(existingTodo?.title || ""); + const [content, setContent] = useState(existingTodo?.content || ""); + const [dueAt, setDueAt] = useState(existingTodo?.dueAt || null); + const [reminderAt, setReminderAt] = useState(existingTodo?.reminderAt || null); + const [selectedTagIds, setSelectedTagIds] = useState( + (existingTodo && tagsWorkspace.assignments[existingTodo.id]) || [] + ); + const [showDeadlinePicker, setShowDeadlinePicker] = useState(false); + const [showTagMenu, setShowTagMenu] = useState(false); + + const titleInputRef = useRef(null); + const editorHandleRef = useRef(null); + const isSavingRef = useRef(false); + + const existingTodoId = existingTodo?.id; + + useEffect(() => { + if (isOpen) { + if (existingTodo) { + setTitle(existingTodo.title); + setContent(existingTodo.content || ""); + setDueAt(existingTodo.dueAt || null); + setReminderAt(existingTodo.reminderAt || null); + setSelectedTagIds(tagsWorkspace.assignments[existingTodo.id] || []); + setMode(storeEditorMode || "view"); + } else { + setTitle(""); + setContent(""); + setDueAt(null); + setReminderAt(null); + setSelectedTagIds([]); + setMode("edit"); + } + setShowDeadlinePicker(false); + setShowTagMenu(false); + + if (!existingTodo) { + setTimeout(() => { + titleInputRef.current?.focus(); + }, 60); + } + } + }, [existingTodoId, isOpen, storeEditorMode]); + + // Global keyboard shortcuts + useEffect(() => { + if (!isOpen) return; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.isComposing || (e as any).keyCode === 229) return; + + // In view mode: pressing 'e' or 'E' (not typing in an input) switches to edit + if (mode === "view") { + const activeTag = document.activeElement?.tagName?.toLowerCase(); + if (activeTag !== "input" && activeTag !== "textarea") { + if (e.key.toLowerCase() === "e") { + e.preventDefault(); + setMode("edit"); + return; + } + } + } + + // In edit mode: Cmd+Enter to save + if (mode === "edit" && (e.metaKey || e.ctrlKey) && e.key === "Enter") { + e.preventDefault(); + handleSave(); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [isOpen, mode, title, content, dueAt, reminderAt, selectedTagIds]); + + if (!isOpen) return null; + + const handleSave = async () => { + if (isSavingRef.current) return; + const trimmedTitle = title.trim(); + const currentMarkdown = editorHandleRef.current?.getMarkdown() ?? content; + const trimmedContent = currentMarkdown.trim(); + + if (!trimmedTitle && !trimmedContent) { + onClose(); + return; + } + + const finalTitle = trimmedTitle || t("untitledTodo"); + + isSavingRef.current = true; + try { + if (isCreate) { + const newTodo = await addTodo(finalTitle, trimmedContent); + if (dueAt) { + await updateTodo(newTodo.id, { dueAt, reminderAt }); + } + if (selectedTagIds.length > 0) { + await setTodoTags(newTodo.id, selectedTagIds); + } + } else { + await updateTodo(existingTodo.id, { + title: finalTitle, + content: trimmedContent, + dueAt, + reminderAt, + }); + await setTodoTags(existingTodo.id, selectedTagIds); + } + onClose(); + } finally { + isSavingRef.current = false; + } + }; + + const toggleTag = (tagId: string) => { + if (selectedTagIds.includes(tagId)) { + setSelectedTagIds(selectedTagIds.filter((id) => id !== tagId)); + } else { + setSelectedTagIds([...selectedTagIds, tagId]); + } + }; + + const deadlineInfo = dueAt ? formatDeadline(dueAt, i18n.language) : null; + const canSave = title.trim().length > 0 || content.trim().length > 0; + + return ( +
    + {/* Top Header Bar */} +
    +
    + + {isCreate + ? t("newTodoDrawerTitle") + : mode === "view" + ? t("todoDetailsDrawerTitle") + : t("editTodoDrawerTitle")} + + + {/* View / Edit Mode Switcher (only for existing todos) */} + {!isCreate && ( +
    + + +
    + )} +
    + + +
    + + {/* Main Content Area */} + {mode === "view" ? ( + /* ==================== VIEW MODE ==================== */ +
    + {/* Title as Crisp Typography Heading */} +
    setMode("edit")} + className="px-5 pt-4 pb-2 shrink-0 select-text cursor-text" + title={t("doubleClickToEdit")} + > +

    + {title || t("untitledTodo")} +

    +
    + + {/* Properties: Deadline + Tags */} + {(deadlineInfo || selectedTagIds.length > 0) && ( +
    + {/* Deadline Badge */} + {deadlineInfo && ( + + + {deadlineInfo.label} + + )} + + {/* Tag Badges */} + {selectedTagIds.map((tagId) => { + const tag = tagsWorkspace.tags.find((t) => t.id === tagId); + if (!tag) return null; + return ( + + + {tag.name} + + ); + })} +
    + )} + + {/* Clean Hairline Divider */} +
    + + {/* View Mode Markdown Content */} +
    setMode("edit")} + className="flex-1 flex flex-col min-h-0 px-5 pt-3 pb-2 overflow-y-auto smooth-scroll select-text" + > + {content.trim() ? ( + + ) : ( +
    setMode("edit")} + className="h-full min-h-[140px] flex flex-col items-center justify-center text-[var(--color-text-subtle)] text-xs cursor-pointer hover:text-[var(--color-teal-primary)] transition-colors select-none" + > + {t("clickToAddContent")} +
    + )} +
    + + {/* View Mode Footer */} +
    + + {t("viewModeHint")} + + +
    + + +
    +
    +
    + ) : ( + /* ==================== EDIT MODE ==================== */ +
    + {/* Title Input (No blue text selection!) */} +
    + { + const val = e.target.value; + if (!title && val === " ") return; + setTitle(val); + }} + onKeyDown={(e) => { + if (e.nativeEvent.isComposing || e.keyCode === 229) return; + if ((e.metaKey || e.ctrlKey) && e.key === "Enter") { + e.preventDefault(); + e.stopPropagation(); + handleSave(); + return; + } + if (e.key === "Enter") { + e.preventDefault(); + editorHandleRef.current?.focus(); + } + }} + placeholder={t("todoTitleFieldHint")} + className="w-full text-[17px] font-semibold bg-transparent text-[var(--color-text-primary)] placeholder:text-[var(--color-text-subtle)] focus:outline-none focus:placeholder-transparent transition-colors tracking-tight" + /> +
    + + {/* Metadata Properties Row: Tags + Deadline directly beneath Title */} +
    + {/* Tag Selector Trigger Button */} +
    + + + {/* Tag Dropdown Popover */} + {showTagMenu && ( + <> +
    setShowTagMenu(false)} + /> +
    +
    + {t("tags")} +
    + {tagsWorkspace.tags.length === 0 ? ( +
    + {t("noTagsYetMessage")} +
    + ) : ( + tagsWorkspace.tags.map((tag) => { + const isSelected = selectedTagIds.includes(tag.id); + return ( + + ); + }) + )} +
    + + )} +
    + + {/* Selected Tag Pills */} + {selectedTagIds.map((tagId) => { + const tag = tagsWorkspace.tags.find((t) => t.id === tagId); + if (!tag) return null; + return ( + + ); + })} + + {/* Deadline Trigger Button */} +
    + +
    +
    + + {/* Clean Hairline Divider */} +
    + + {/* Tiptap Editor Canvas */} +
    + setContent(md)} + onCmdEnter={handleSave} + /> +
    + + {/* Deadline Picker Popover Dialog */} + {showDeadlinePicker && ( + { + setDueAt(d); + setReminderAt(r); + setShowDeadlinePicker(false); + }} + onClose={() => setShowDeadlinePicker(false)} + /> + )} + + {/* Edit Mode Footer Bar */} +
    + + {t("shortcutHint")} + + +
    + + +
    +
    +
    + )} +
    + ); +}; diff --git a/src/components/todos/TodoItemRow.tsx b/src/components/todos/TodoItemRow.tsx new file mode 100644 index 0000000..aae8dc5 --- /dev/null +++ b/src/components/todos/TodoItemRow.tsx @@ -0,0 +1,321 @@ +import React, { useState, useRef } from "react"; +import { useTranslation } from "react-i18next"; +import { + Check, + Clock, + Copy, + DotsThree, + PencilSimple, + Archive, + ArrowUUpLeft, + Trash, + Tag, +} from "@phosphor-icons/react"; +import type { TodoItem } from "@/types"; +import { useTodoStore } from "@/stores/useTodoStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { formatDeadline, formatTime } from "@/lib/dateUtils"; + +interface TodoItemRowProps { + todo: TodoItem; + onView: (todo: TodoItem) => void; + onEdit: (todo: TodoItem) => void; + onOpenDeadlinePicker: (todo: TodoItem) => void; + onOpenTagAssignment: (todo: TodoItem) => void; +} + +export const TodoItemRow: React.FC = ({ + todo, + onView, + onEdit, + onOpenDeadlinePicker, + onOpenTagAssignment, +}) => { + const { t, i18n } = useTranslation(); + + const toggleComplete = useTodoStore((s) => s.toggleComplete); + const archiveTodo = useTodoStore((s) => s.archiveTodo); + const restoreTodo = useTodoStore((s) => s.restoreTodo); + const deleteTodo = useTodoStore((s) => s.deleteTodo); + + const tagsWorkspace = useTagStore((s) => s.workspace); + const [showMenu, setShowMenu] = useState(false); + const [copied, setCopied] = useState(false); + const [menuPlacement, setMenuPlacement] = useState<"top" | "bottom">("bottom"); + const moreButtonRef = useRef(null); + + const isCompleted = !!todo.completedAt; + const isArchived = !!todo.archivedAt; + + const assignedTagIds = tagsWorkspace.assignments[todo.id] || []; + const assignedTags = tagsWorkspace.tags.filter((t) => assignedTagIds.includes(t.id)); + + const deadlineInfo = todo.dueAt ? formatDeadline(todo.dueAt, i18n.language) : null; + + const handleCopyMarkdown = (e: React.MouseEvent) => { + e.stopPropagation(); + const statusMark = isCompleted ? "[x]" : "[ ]"; + const text = `- ${statusMark} ${todo.title}${todo.content ? `\n ${todo.content}` : ""}`; + navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 1200); + }; + + const handleToggleMenu = (e: React.MouseEvent) => { + e.stopPropagation(); + if (!showMenu && moreButtonRef.current) { + const rect = moreButtonRef.current.getBoundingClientRect(); + const spaceBelow = window.innerHeight - rect.bottom; + if (spaceBelow < 140) { + setMenuPlacement("top"); + } else { + setMenuPlacement("bottom"); + } + } + setShowMenu((prev) => !prev); + }; + + const isMockHover = + typeof window !== "undefined" && + window.location.search.includes("mock") && + window.location.search.includes("hover") && + todo.id === "mock-todo-2"; + + return ( +
    onView(todo)} + className="group relative pl-[7px] pr-[5px] py-2 my-[2px] rounded-[8px] transition-colors duration-150 select-none hover:bg-[var(--color-row-hover)] cursor-pointer" + > + {/* Main Row: Checkbox + Full-width Title (Completely unobstructed on hover!) */} +
    + {/* 16x16 Checkbox with r=4px, border=1.3px, optically centered with text */} +
    + +
    + + {/* SizedBox(width: 6) */} +
    + + {/* Title (fontSize: 13.5) - 100% full row width */} +
    + + {todo.title} + +
    +
    + + {/* Sub-row: Indented 30px (Metadata Line + Floating Hover Action Capsule) */} +
    + {/* Left: Deadline status + Tags + Tag shortcut button */} +
    + {/* Deadline */} + {deadlineInfo && ( + + )} + + {/* FloatickTagChip without background and border */} + {assignedTags.map((tag) => ( + + ))} + + {/* Tag Quick Selection Icon Button (Flutter: assign-tags-$todoId) */} + {!isArchived && ( + + )} +
    + + {/* Right: Created / Archived timestamp (Fades out when hovered or menu opened) */} +
    + {formatTime(isArchived && todo.archivedAt ? todo.archivedAt : todo.createdAt)} +
    + + {/* Floating Hover Actions with Frosted Blur Backdrop on the Next Line */} +
    + {/* Frosted Glass Floating Action Pill */} +
    + {/* Edit button */} + + + {/* Deadline */} + {!isArchived && ( + + )} + + {/* Copy */} + + + {/* More actions */} +
    + + + {showMenu && ( + <> +
    { + e.stopPropagation(); + setShowMenu(false); + }} + /> +
    + {isArchived ? ( + + ) : ( + + )} + + +
    + + )} +
    +
    +
    +
    +
    + ); +}; diff --git a/src/components/todos/TodoPanel.tsx b/src/components/todos/TodoPanel.tsx new file mode 100644 index 0000000..7ac1b60 --- /dev/null +++ b/src/components/todos/TodoPanel.tsx @@ -0,0 +1,178 @@ +import React, { useState, useMemo, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { CheckCircle } from "@phosphor-icons/react"; +import type { TodoItem } from "@/types"; +import { useTodoStore } from "@/stores/useTodoStore"; +import { useTagStore } from "@/stores/useTagStore"; +import { ActionBar } from "@/components/common/ActionBar"; +import { TodoItemRow } from "./TodoItemRow"; +import { TodoDeadlinePicker } from "./TodoDeadlinePicker"; +import { getGroupLabel } from "@/lib/dateUtils"; + +interface TodoPanelProps { + onOpenTagFilter: () => void; + onOpenTagAssignment: (todoId: string) => void; +} + +export const TodoPanel: React.FC = ({ + onOpenTagFilter, + onOpenTagAssignment, +}) => { + const { t, i18n } = useTranslation(); + + const todos = useTodoStore((s) => s.todos); + const updateTodo = useTodoStore((s) => s.updateTodo); + const searchQuery = useTodoStore((s) => s.searchQuery); + const setSearchQuery = useTodoStore((s) => s.setSearchQuery); + const activeScope = useTodoStore((s) => s.activeScope); + + const tagsWorkspace = useTagStore((s) => s.workspace); + const selectedTagIds = useTagStore((s) => s.selectedTagIds); + + const setIsEditorOpen = useTodoStore((s) => s.setIsEditorOpen); + const setEditingTodoId = useTodoStore((s) => s.setEditingTodoId); + + // Standalone Deadline Picker on row + const [deadliningTodo, setDeadliningTodo] = useState(null); + + // Filter todos + const filteredTodos = useMemo(() => { + return todos.filter((item) => { + // Scope filter + const isArchived = !!item.archivedAt; + if (activeScope === "active" && isArchived) return false; + if (activeScope === "archived" && !isArchived) return false; + + // Multi-Tag filter (OR match, exact same as Flutter) + if (selectedTagIds.length > 0) { + const assigned = tagsWorkspace.assignments[item.id] || []; + const matchesTag = selectedTagIds.some((id) => assigned.includes(id)); + if (!matchesTag) return false; + } + + // Search query filter + if (searchQuery.trim()) { + const q = searchQuery.toLowerCase(); + const matchTitle = item.title.toLowerCase().includes(q); + const matchContent = (item.content || "").toLowerCase().includes(q); + if (!matchTitle && !matchContent) return false; + } + + return true; + }); + }, [todos, activeScope, selectedTagIds, searchQuery, tagsWorkspace]); + + // Grouping by Date + const groupedTodos = useMemo(() => { + const groups: { label: string; items: TodoItem[] }[] = []; + const map = new Map(); + + for (const item of filteredTodos) { + const label = getGroupLabel(item.createdAt, i18n.language); + if (!map.has(label)) { + map.set(label, []); + } + map.get(label)!.push(item); + } + + for (const [label, items] of map.entries()) { + groups.push({ label, items }); + } + + return groups; + }, [filteredTodos, i18n.language]); + + const setEditorMode = useTodoStore((s) => s.setEditorMode); + + const handleOpenCreate = () => { + setEditingTodoId(null); + setEditorMode("edit"); + setIsEditorOpen(true); + }; + + useEffect(() => { + const handler = () => handleOpenCreate(); + window.addEventListener("floatick:new-item", handler); + return () => window.removeEventListener("floatick:new-item", handler); + }, []); + + const handleOpenView = (todo: TodoItem) => { + setEditingTodoId(todo.id); + setEditorMode("view"); + setIsEditorOpen(true); + }; + + const handleOpenEdit = (todo: TodoItem) => { + setEditingTodoId(todo.id); + setEditorMode("edit"); + setIsEditorOpen(true); + }; + + const handleSaveDeadline = async (dueAt: string | null, reminderAt: string | null) => { + if (deadliningTodo) { + await updateTodo(deadliningTodo.id, { dueAt, reminderAt }); + setDeadliningTodo(null); + } + }; + + return ( +
    + {/* Action Bar */} + + + {/* Todo List Area */} +
    + {groupedTodos.length === 0 ? ( +
    + +

    + {t("allClear")} +

    +

    + {t("allClearSub")} +

    +
    + ) : ( + groupedTodos.map((group) => ( +
    + {/* Clean category header */} +
    + {group.label} +
    + + {/* Items */} +
    + {group.items.map((todo) => ( + setDeadliningTodo(t)} + onOpenTagAssignment={() => onOpenTagAssignment(todo.id)} + /> + ))} +
    +
    + )) + )} +
    + + {/* Deadline Picker Modal */} + {deadliningTodo && ( + setDeadliningTodo(null)} + /> + )} +
    + ); +}; diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 0000000..5569a80 --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,18 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import zh from "./locales/zh.json"; +import en from "./locales/en.json"; + +i18n.use(initReactI18next).init({ + resources: { + zh: { translation: zh }, + en: { translation: en }, + }, + lng: "zh", // default fallback, will be updated by settings + fallbackLng: "zh", + interpolation: { + escapeValue: false, + }, +}); + +export default i18n; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json new file mode 100644 index 0000000..ae55eda --- /dev/null +++ b/src/i18n/locales/en.json @@ -0,0 +1,144 @@ +{ + "appName": "Floatick", + "todos": "Todos", + "notes": "Notes", + "settings": "Settings", + "settingsTitle": "Settings", + "today": "Today", + "yesterday": "Yesterday", + "earlier": "Earlier", + "searchTodos": "Search", + "searchNotes": "Search", + "search": "Search", + "clearSearch": "Clear", + "addTodoPlaceholder": "New task…", + "completed": "Completed", + "incomplete": "Incomplete", + "archived": "Archived", + "active": "Active", + "allClear": "All done", + "allClearSub": "No tasks remaining", + "tasksRemaining": "{{count}} tasks", + "noteCount": "{{count}} notes", + "edit": "Edit", + "delete": "Delete", + "archive": "Archive", + "restore": "Restore", + "manageTags": "Manage Tags", + "allTags": "All", + "allTagsFilterLabel": "All todos", + "filterByTagTitle": "Filter by tag", + "filterByTag": "Filter by tag", + "assignTagsTitle": "Assign Tags", + "assignTagsTooltip": "Assign tags", + "tags": "Tags", + "newTag": "New Tag", + "tagName": "Tag name", + "searchOrCreateTagHint": "Search or create tag…", + "tagNameAlreadyExistsMessage": "Tag name already exists", + "tagColor": "Color", + "createdTags": "Tags", + "noTagsYetMessage": "No tags yet", + "newNote": "New Note", + "noNotes": "No notes", + "noNotesSub": "Click + to create your first note", + "pinnedNotes": "Pinned", + "pin": "Pin", + "unpin": "Unpin", + "preview": "Preview", + "write": "Write", + "title": "Title", + "content": "Notes", + "save": "Save", + "cancel": "Cancel", + "confirm": "Confirm", + "finish": "Done", + "deadline": "Deadline", + "setDeadline": "Set deadline", + "editDeadline": "Edit deadline", + "clearDeadline": "Clear", + "overdue": "Overdue", + "reminder": "Reminder", + "atDeadline": "At deadline", + "tenMinutesBefore": "10m before", + "oneHourBefore": "1h before", + "oneDayBefore": "1d before", + "noReminder": "None", + "copyMarkdown": "Copy", + "copied": "Copied", + "moreActions": "More", + "newTodoDrawerTitle": "New Todo", + "editTodoDrawerTitle": "Edit Todo", + "untitledTodo": "Untitled Todo", + "newNoteDrawerTitle": "New Note", + "editNoteDrawerTitle": "Edit Note", + "todoTitleLabel": "Title", + "todoTitleFieldHint": "Task title…", + "todoContentLabel": "Notes", + "todoContentFieldHint": "Add notes (Markdown)…", + "noteTitlePlaceholder": "Untitled note", + "noteContentPlaceholder": "Write something (type / for commands)…", + "markdownWriteLabel": "Write", + "markdownPreviewLabel": "Preview", + "markdownPreviewEmptyMessage": "No content", + "viewMode": "View", + "editMode": "Edit", + "todoDetailsDrawerTitle": "Todo Details", + "noteDetailsDrawerTitle": "Note Details", + "untitledNote": "Untitled Note", + "clickToAddContent": "No content. Click to add details…", + "doubleClickToEdit": "Double-click to edit", + "viewModeHint": "Press E to edit · Esc to close", + "edit": "Edit", + "close": "Close", + "createTodoAction": "New Todo", + "saveChangesAction": "Save", + "shortcutHint": "⌘ ⏎ Save · Esc Exit", + "appearanceSectionTitle": "Appearance", + "themeSystemTooltip": "System", + "themeLightTooltip": "Light", + "themeDarkTooltip": "Dark", + "themeLabel": "Theme", + "languageSectionTitle": "Language", + "languageLabel": "Language", + "languageSystemTooltip": "System", + "languageSimplifiedChineseTooltip": "简体中文", + "languageEnglishTooltip": "English", + "preferencesSectionTitle": "Preferences", + "windowSectionTitle": "Window Experience", + "alwaysOnTopLabel": "Always on top", + "alwaysOnTopDesc": "Keep floating above all other windows", + "collapseWhenClickingOutsideLabel": "Close on blur", + "collapseOnBlurDesc": "Hide window when clicking outside", + "startupSectionTitle": "General & Startup", + "openAtLoginLabel": "Launch at login", + "openAtLoginDesc": "Start Floatick silently upon login", + "aboutSectionTitle": "About", + "versionLabel": "Version", + "workingDirectorySectionTitle": "Data Storage", + "workingDirectoryDesc": "Local JSON database directory", + "shortcutsSectionTitle": "Keyboard Shortcuts", + "shortcutTodos": "Switch to Todos", + "shortcutNotes": "Switch to Notes", + "shortcutNew": "New item", + "shortcutSearch": "Focus search bar", + "shortcutSettings": "Preferences", + "shortcutEsc": "Close drawer or hide window", + "copyPath": "Copy Path", + "appDescription": "Fast & lightweight desktop productivity · Native macOS experience", + "quitFloatick": "Quit Floatick", + "quit": "Quit", + "escToClose": "Close (Esc)", + "deleteConfirmationTitle": "Delete this item?", + "deleteConfirmationMessage": "This action cannot be undone.", + "itemsCount": "{{count}} items", + "bold": "Bold", + "italic": "Italic", + "bulletList": "List", + "taskList": "Tasks", + "inlineCode": "Code", + "link": "Link", + "charactersCount": "{{count}} chars", + "dateFormatMonthYear": "MMMM yyyy", + "dateFormatMonthDay": "MMM d" +} diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json new file mode 100644 index 0000000..f816b2d --- /dev/null +++ b/src/i18n/locales/zh.json @@ -0,0 +1,144 @@ +{ + "appName": "Floatick", + "todos": "待办", + "notes": "便签", + "settings": "设置", + "settingsTitle": "设置", + "today": "今天", + "yesterday": "昨天", + "earlier": "更早", + "searchTodos": "搜索", + "searchNotes": "搜索", + "search": "搜索", + "clearSearch": "清除", + "addTodoPlaceholder": "新建待办…", + "completed": "已完成", + "incomplete": "未完成", + "archived": "已归档", + "active": "待办", + "allClear": "全部完成", + "allClearSub": "暂无待办事项", + "tasksRemaining": "{{count}} 个待办", + "noteCount": "{{count}} 条便签", + "edit": "编辑", + "delete": "删除", + "archive": "归档", + "restore": "恢复", + "manageTags": "管理标签", + "allTags": "全部", + "allTagsFilterLabel": "全部待办", + "filterByTagTitle": "标签筛选", + "filterByTag": "标签筛选", + "assignTagsTitle": "分配标签", + "assignTagsTooltip": "分配标签", + "tags": "标签", + "newTag": "新建标签", + "tagName": "标签名称", + "searchOrCreateTagHint": "搜索或创建标签…", + "tagNameAlreadyExistsMessage": "该标签名称已存在", + "tagColor": "颜色", + "createdTags": "现有标签", + "noTagsYetMessage": "暂无标签", + "newNote": "新建便签", + "noNotes": "暂无便签", + "noNotesSub": "点击右上角 + 创建第一条便签", + "pinnedNotes": "置顶", + "pin": "置顶", + "unpin": "取消置顶", + "preview": "预览", + "write": "编辑", + "title": "标题", + "content": "备注", + "save": "保存", + "cancel": "取消", + "confirm": "确认", + "finish": "完成", + "deadline": "截止时间", + "setDeadline": "设置截止时间", + "editDeadline": "修改截止时间", + "clearDeadline": "清除", + "overdue": "逾期", + "reminder": "提醒", + "atDeadline": "截止时", + "tenMinutesBefore": "提前 10 分钟", + "oneHourBefore": "提前 1 小时", + "oneDayBefore": "提前 1 天", + "noReminder": "不提醒", + "copyMarkdown": "复制", + "copied": "已复制", + "moreActions": "更多", + "newTodoDrawerTitle": "新建待办", + "editTodoDrawerTitle": "编辑待办", + "untitledTodo": "无标题待办", + "newNoteDrawerTitle": "新建便签", + "editNoteDrawerTitle": "编辑便签", + "todoTitleLabel": "标题", + "todoTitleFieldHint": "输入待办事项…", + "todoContentLabel": "备注", + "todoContentFieldHint": "添加备注 (Markdown)…", + "noteTitlePlaceholder": "无标题便签", + "noteContentPlaceholder": "记点什么 (输入 / 快速插入)…", + "markdownWriteLabel": "编辑", + "markdownPreviewLabel": "预览", + "markdownPreviewEmptyMessage": "暂无内容", + "viewMode": "查看", + "editMode": "编辑", + "todoDetailsDrawerTitle": "待办详情", + "noteDetailsDrawerTitle": "便签详情", + "untitledNote": "无标题便签", + "clickToAddContent": "暂无正文内容,点击进入编辑…", + "doubleClickToEdit": "双击进入编辑", + "viewModeHint": "按 E 编辑 · Esc 退出", + "edit": "编辑", + "close": "关闭", + "createTodoAction": "新建待办", + "saveChangesAction": "保存", + "shortcutHint": "⌘ ⏎ 保存 · Esc 退出", + "appearanceSectionTitle": "外观", + "themeSystemTooltip": "系统", + "themeLightTooltip": "浅色", + "themeDarkTooltip": "深色", + "themeLabel": "主题", + "languageSectionTitle": "语言", + "languageLabel": "语言", + "languageSystemTooltip": "跟随系统", + "languageSimplifiedChineseTooltip": "简体中文", + "languageEnglishTooltip": "English", + "preferencesSectionTitle": "偏好", + "windowSectionTitle": "窗口体验", + "alwaysOnTopLabel": "窗口始终置顶", + "alwaysOnTopDesc": "浮动在其他应用与全屏窗口最前", + "collapseWhenClickingOutsideLabel": "失焦自动收起", + "collapseOnBlurDesc": "点击外部区域时自动收起至托盘", + "startupSectionTitle": "通用与启动", + "openAtLoginLabel": "开机自动启动", + "openAtLoginDesc": "登录系统后在后台就绪", + "aboutSectionTitle": "关于", + "versionLabel": "版本", + "workingDirectorySectionTitle": "数据存储", + "workingDirectoryDesc": "待办与便签本地 JSON 存储路径", + "shortcutsSectionTitle": "快捷键速查", + "shortcutTodos": "切换待办清单", + "shortcutNotes": "切换快速便签", + "shortcutNew": "新建待办或便签", + "shortcutSearch": "聚焦搜索框", + "shortcutSettings": "偏好设置", + "shortcutEsc": "关闭抽屉或隐藏窗口", + "copyPath": "复制路径", + "appDescription": "轻量极速的桌面待办与便签 · 原生体验", + "quitFloatick": "退出 Floatick", + "quit": "退出", + "escToClose": "收起 (Esc)", + "deleteConfirmationTitle": "确认删除?", + "deleteConfirmationMessage": "此操作无法撤销。", + "itemsCount": "{{count}} 项", + "bold": "加粗", + "italic": "斜体", + "bulletList": "列表", + "taskList": "任务清单", + "inlineCode": "代码", + "link": "链接", + "charactersCount": "{{count}} 字", + "dateFormatMonthYear": "yyyy年 M月", + "dateFormatMonthDay": "M月d日" +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..28b3c1f --- /dev/null +++ b/src/index.css @@ -0,0 +1,360 @@ +@import "@fontsource/fira-code/latin-400.css"; +@import "@fontsource/fira-code/latin-500.css"; +@import "tailwindcss"; + +@variant dark (&:where(.dark, .dark *)); + +:root { + /* Exact Native macOS SF Pro + PingFang typography */ + --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", "SF Pro", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; + --font-mono: "Fira Code", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + + /* Light Theme Tokens (Floatick lightSurface #F9FBFA, ink #11181C, teal #0F8F83) */ + --color-bg-panel: #F9FBFA; + --color-border-panel: rgba(0, 0, 0, 0.08); + --color-shadow-panel: none; + + --color-bg-elevated: #EFF3F1; + --color-bg-elevated-hover: #E2E8E5; + --color-bg-drawer: #FFFFFF; + --color-border-drawer: rgba(0, 0, 0, 0.08); + + --color-text-primary: #11181C; + --color-text-secondary: rgba(17, 24, 28, 0.72); + --color-text-subtle: rgba(17, 24, 28, 0.52); + + --color-hover-overlay: rgba(0, 0, 0, 0.045); + --color-scrollbar-thumb: rgba(0, 0, 0, 0.16); + --color-scrollbar-thumb-hover: rgba(0, 0, 0, 0.30); + + --color-teal-primary: #0F8F83; + --color-teal-tint: rgba(15, 143, 131, 0.10); + --color-teal-tint-active: rgba(15, 143, 131, 0.18); + + --color-row-hover: rgba(0, 0, 0, 0.035); +} + +.dark { + /* Scheme A: macOS Native Space Gray (Raycast / Apple Pro dark surface) */ + --color-bg-panel: #1A1A1C; + --color-border-panel: rgba(255, 255, 255, 0.08); + --color-shadow-panel: none; + + --color-bg-elevated: #242427; + --color-bg-elevated-hover: #2C2C30; + --color-bg-drawer: #222225; + --color-border-drawer: rgba(255, 255, 255, 0.08); + + --color-text-primary: #F5F5F7; + --color-text-secondary: rgba(240, 240, 245, 0.74); + --color-text-subtle: rgba(240, 240, 245, 0.52); + + --color-hover-overlay: rgba(255, 255, 255, 0.06); + --color-scrollbar-thumb: rgba(255, 255, 255, 0.16); + --color-scrollbar-thumb-hover: rgba(255, 255, 255, 0.28); + + --color-teal-primary: #00C7B7; + --color-teal-tint: rgba(0, 199, 183, 0.12); + --color-teal-tint-active: rgba(0, 199, 183, 0.20); + + --color-row-hover: rgba(255, 255, 255, 0.05); +} + +@layer base { + * { + box-sizing: border-box; + text-rendering: optimizeLegibility; + } + + html, body, #root { + height: 100%; + width: 100%; + margin: 0; + padding: 0; + background-color: transparent !important; + overflow: hidden; + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* Enable GPU rendering context on root */ + transform: translateZ(0); + backface-visibility: hidden; + } + + textarea::placeholder, + input::placeholder { + color: var(--color-text-subtle); + opacity: 0.85; + line-height: inherit; + } +} + +/* Floatick Panel Surface */ +.floatick-panel { + background-color: var(--color-bg-panel); + border: 1px solid var(--color-border-panel); + color: var(--color-text-primary); + /* Dedicated Compositor Layer for 120Hz rendering */ + transform: translateZ(0); + backface-visibility: hidden; +} + +/* Zero-delay atomic theme switch: suppresses all CSS transitions during theme update to eliminate staggered lag */ +.disable-transitions, +.disable-transitions *, +.disable-transitions *::before, +.disable-transitions *::after { + -webkit-transition: none !important; + -moz-transition: none !important; + -o-transition: none !important; + -ms-transition: none !important; + transition: none !important; +} + +/* Micro-interaction tactile button (Floatick hover motion) */ +.tactile-btn { + transition: transform 120ms cubic-bezier(0.16, 1, 0.3, 1), opacity 150ms ease; + user-select: none; +} + +.tactile-btn:hover { + transition: transform 120ms cubic-bezier(0.16, 1, 0.3, 1), background-color 150ms ease, color 150ms ease, opacity 150ms ease; +} + +.tactile-btn:active { + transform: scale(0.965); +} + +/* 120Hz / 60Hz Smooth Hardware Accelerated Scrolling */ +.smooth-scroll { + -webkit-overflow-scrolling: touch; + overscroll-behavior-y: contain; + scroll-behavior: smooth; + transform: translateZ(0); +} + +/* Hardware-accelerated sliding sheets (120Hz ProMotion friendly) */ +.sheet-slide-up { + transform: translateZ(0); + will-change: transform; + backface-visibility: hidden; +} + +/* Custom sleek scrollbar */ +::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--color-scrollbar-thumb); + border-radius: 9999px; + transition: background-color 150ms ease; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-scrollbar-thumb-hover); +} + +/* Micro-scrollbar for dropdown menus and slash popups */ +.slash-menu-scrollbar { + scrollbar-width: thin; + scrollbar-color: var(--color-scrollbar-thumb) transparent; +} + +.slash-menu-scrollbar::-webkit-scrollbar { + width: 3.5px; +} + +.slash-menu-scrollbar::-webkit-scrollbar-track { + background: transparent; + margin: 6px 0; +} + +.slash-menu-scrollbar::-webkit-scrollbar-thumb { + background: var(--color-scrollbar-thumb); + border-radius: 9999px; + transition: background-color 150ms ease; +} + +.slash-menu-scrollbar::-webkit-scrollbar-thumb:hover { + background: var(--color-scrollbar-thumb-hover); +} + +/* Utility to hide scrollbars while retaining scroll functionality */ +.no-scrollbar::-webkit-scrollbar { + display: none; +} +.no-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} + +/* Tiptap Editor & Task List styling */ +.tiptap { + outline: none; + font-family: var(--font-sans); + color: var(--color-text-primary); + font-size: 13.5px; + line-height: 1.65; + min-height: 100%; +} + +/* Task List & Checkbox */ +.tiptap ul[data-type="taskList"] { + list-style: none; + padding: 0; + margin: 0.5rem 0; +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] { + display: flex; + align-items: flex-start; + margin-bottom: 0.35rem; + line-height: 1.5; +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] > label { + flex: 0 0 auto; + margin-right: 0.55rem; + user-select: none; + cursor: pointer; + display: flex; + align-items: center; + margin-top: 0.2rem; +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] > label input[type="checkbox"] { + cursor: pointer; + width: 15px; + height: 15px; + border-radius: 4px; + border: 1.5px solid var(--color-text-subtle); + appearance: none; + -webkit-appearance: none; + outline: none; + transition: all 120ms ease; + background-color: transparent; + display: grid; + place-content: center; + position: relative; +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] > label input[type="checkbox"]:hover { + border-color: var(--color-teal-primary); +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] > label input[type="checkbox"]:checked { + background-color: var(--color-teal-primary); + border-color: var(--color-teal-primary); +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] > label input[type="checkbox"]:checked::after { + content: ""; + width: 4px; + height: 8px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg) translate(-0.5px, -0.5px); +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"][data-checked="true"] > div { + text-decoration: line-through; + opacity: 0.55; + transition: opacity 150ms ease; +} + +.tiptap ul[data-type="taskList"] li[data-type="taskItem"] > div { + flex: 1 1 auto; + min-width: 0; +} + +/* Standard Lists */ +.tiptap ul:not([data-type="taskList"]) { + list-style-type: disc; + padding-left: 1.25rem; + margin: 0.5rem 0; +} + +.tiptap ol { + list-style-type: decimal; + padding-left: 1.25rem; + margin: 0.5rem 0; +} + +.tiptap li { + margin-bottom: 0.25rem; +} + +/* Headings */ +.tiptap h1 { + font-size: 1.25rem; + font-weight: 700; + margin-top: 1rem; + margin-bottom: 0.4rem; + letter-spacing: -0.02em; + color: var(--color-text-primary); +} + +.tiptap h2 { + font-size: 1.1rem; + font-weight: 600; + margin-top: 0.85rem; + margin-bottom: 0.35rem; + letter-spacing: -0.015em; + color: var(--color-text-primary); +} + +.tiptap h3 { + font-size: 1rem; + font-weight: 600; + margin-top: 0.75rem; + margin-bottom: 0.25rem; + color: var(--color-text-primary); +} + +.tiptap p { + margin-bottom: 0.5rem; + line-height: 1.65; +} + +.tiptap blockquote { + border-left: 3px solid var(--color-teal-primary); + background-color: var(--color-teal-tint); + padding: 0.5rem 0.85rem; + border-radius: 0 8px 8px 0; + margin: 0.6rem 0; + font-style: italic; + color: var(--color-text-secondary); +} + +.tiptap pre { + background-color: var(--color-hover-overlay); + border: 1px solid var(--color-border-panel); + border-radius: 8px; + padding: 0.75rem 1rem; + font-family: var(--font-mono); + font-size: 12px; + margin: 0.6rem 0; + color: var(--color-text-primary); +} + +.tiptap code { + font-family: var(--font-mono); + font-size: 11.5px; + background-color: var(--color-hover-overlay); + padding: 0.15rem 0.35rem; + border-radius: 4px; + color: var(--color-teal-primary); +} + +.tiptap hr { + border: none; + border-top: 1px solid var(--color-border-panel); + margin: 1rem 0; +} + diff --git a/src/lib/api.ts b/src/lib/api.ts new file mode 100644 index 0000000..42ed88d --- /dev/null +++ b/src/lib/api.ts @@ -0,0 +1,209 @@ +import { invoke } from "@tauri-apps/api/core"; +import type { TodoItem, TagWorkspace, NoteItem, AppSettings } from "@/types"; + +const isMock = typeof window !== "undefined" && ( + window.location.search.includes("mock") || !("__TAURI_INTERNALS__" in window) +); + +const nowIso = new Date().toISOString(); +const todayDueIso = new Date(Date.now() + 3600 * 1000 * 4).toISOString(); +const tomorrowDueIso = new Date(Date.now() + 3600 * 1000 * 24).toISOString(); + +const MOCK_TAGS: TagWorkspace = { + version: 1, + tags: [ + { id: "tag-1", name: "Design", colorHex: "#6366F1", createdAt: nowIso }, + { id: "tag-2", name: "Engineering", colorHex: "#14B8A6", createdAt: nowIso }, + { id: "tag-3", name: "Product", colorHex: "#F59E0B", createdAt: nowIso }, + { id: "tag-4", name: "Launch", colorHex: "#EC4899", createdAt: nowIso }, + ], + assignments: { + "mock-todo-1": ["tag-1"], + "mock-todo-2": ["tag-3"], + "mock-todo-3": ["tag-2"], + "mock-todo-4": ["tag-4"], + }, +}; + +const MOCK_TODOS: TodoItem[] = [ + { + id: "mock-todo-1", + title: "Align design system tokens & frosted blur glass", + content: "Ensure backdrop blur and border contrast adapt gracefully across light and dark macOS themes.\n\n### Sprint Milestones\n- [x] Integrate Tauri v2 AppKit native tray\n- [x] Support TipTap markdown slash commands\n- [ ] Polish product showcase & documentation\n\n```bash\npnpm tauri:build\n```\n\n> Fast, distraction-free, and local-first for macOS.", + createdAt: nowIso, + startedAt: null, + completedAt: null, + archivedAt: null, + dueAt: todayDueIso, + reminderAt: null, + }, + { + id: "mock-todo-2", + title: "Review Q4 product roadmap & release checklist", + content: "Review open PRs, verify universal dmg packaging, and update changelog documentation.", + createdAt: nowIso, + startedAt: null, + completedAt: null, + archivedAt: null, + dueAt: tomorrowDueIso, + reminderAt: null, + }, + { + id: "mock-todo-3", + title: "Implement TipTap slash commands & micro-toolbars", + content: "Interactive task items, heading levels, quotes, code fences, and keyboard shortcuts.", + createdAt: nowIso, + startedAt: null, + completedAt: null, + archivedAt: null, + dueAt: null, + reminderAt: null, + }, + { + id: "mock-todo-4", + title: "Draft launch communication and release notes", + content: "Highlight local-first architecture and native macOS menu bar status extra.", + createdAt: nowIso, + startedAt: null, + completedAt: null, + archivedAt: null, + dueAt: null, + reminderAt: null, + }, + { + id: "mock-todo-5", + title: "Migrate core framework to Tauri v2 and React 19", + content: "Achieved sub-30MB idle footprint and 120Hz smooth rendering.", + createdAt: new Date(Date.now() - 3600 * 1000 * 8).toISOString(), + startedAt: null, + completedAt: new Date(Date.now() - 3600 * 1000 * 2).toISOString(), + archivedAt: null, + dueAt: null, + reminderAt: null, + }, + { + id: "mock-todo-6", + title: "Add macOS native Menu Bar template status icon", + content: "Dynamic badge counter showing remaining pending items.", + createdAt: new Date(Date.now() - 3600 * 1000 * 12).toISOString(), + startedAt: null, + completedAt: new Date(Date.now() - 3600 * 1000 * 4).toISOString(), + archivedAt: null, + dueAt: null, + reminderAt: null, + }, +]; + +const MOCK_NOTES: NoteItem[] = [ + { + id: "mock-note-1", + title: "Architecture & Intentional Simplicity", + content: "Floatick is designed for intentional simplicity. Instead of unbounded task hierarchies, it honors lightweight task management, TipTap Markdown writing, and unobtrusive macOS menu bar integration.\n\n- Local-first in ~/.floatick\n- 120Hz ProMotion fluidity\n- Native NSStatusBar status extra", + tagIds: ["tag-1", "tag-2"], + createdAt: nowIso, + updatedAt: nowIso, + pinnedAt: nowIso, + archivedAt: null, + }, + { + id: "mock-note-2", + title: "Keyboard-First Workflows & Shortcuts", + content: "⌘N to capture instantly, ⌘⏎ to save and confirm, Esc to dismiss, / in editor for rich blocks. Fast capture without context switching.", + tagIds: ["tag-3"], + createdAt: new Date(Date.now() - 3600 * 1000 * 6).toISOString(), + updatedAt: new Date(Date.now() - 3600 * 1000 * 6).toISOString(), + pinnedAt: null, + archivedAt: null, + }, + { + id: "mock-note-3", + title: "Release v0.4.0 Highlights", + content: "Full-width title layout, secondary row frosted action capsules, and modular cross-tab colored tags.", + tagIds: ["tag-4"], + createdAt: new Date(Date.now() - 3600 * 1000 * 24).toISOString(), + updatedAt: new Date(Date.now() - 3600 * 1000 * 24).toISOString(), + pinnedAt: null, + archivedAt: null, + }, +]; + +const MOCK_SETTINGS: AppSettings = { + theme: "dark", + language: "en", + alwaysOnTop: true, + collapseWhenClickingOutside: true, +}; + +export const api = { + // Todos + getTodos: async (): Promise => { + if (isMock) return MOCK_TODOS; + return await invoke("get_todos"); + }, + saveTodos: async (todos: TodoItem[]): Promise => { + if (isMock) return; + await invoke("save_todos", { todos }); + }, + + // Tags + getTags: async (): Promise => { + if (isMock) return MOCK_TAGS; + return await invoke("get_tags"); + }, + saveTags: async (workspace: TagWorkspace): Promise => { + if (isMock) return; + await invoke("save_tags", { workspace }); + }, + + // Notes + getNotes: async (): Promise => { + if (isMock) return MOCK_NOTES; + return await invoke("get_notes"); + }, + saveNotes: async (notes: NoteItem[]): Promise => { + if (isMock) return; + await invoke("save_notes", { notes }); + }, + + // Settings + getSettings: async (): Promise => { + if (isMock) return MOCK_SETTINGS; + return await invoke("get_settings"); + }, + saveSettings: async (settings: AppSettings): Promise => { + if (isMock) return; + await invoke("save_settings", { settings }); + }, + + // Window actions + hideWindow: async (): Promise => { + if (isMock) return; + await invoke("hide_window"); + }, + toggleWindow: async (): Promise => { + if (isMock) return; + await invoke("toggle_window"); + }, + setAlwaysOnTop: async (alwaysOnTop: boolean): Promise => { + if (isMock) return; + await invoke("set_always_on_top", { alwaysOnTop }); + }, + updateTrayCount: async (count: number): Promise => { + if (isMock) return; + await invoke("update_tray_count", { count }); + }, + + // Autostart + isAutostartEnabled: async (): Promise => { + if (isMock) return false; + return await invoke("is_autostart_enabled"); + }, + setAutostartEnabled: async (enabled: boolean): Promise => { + if (isMock) return; + await invoke("set_autostart_enabled", { enabled }); + }, + quitApp: async (): Promise => { + if (isMock) return; + await invoke("quit_app"); + }, +}; diff --git a/src/lib/dateUtils.ts b/src/lib/dateUtils.ts new file mode 100644 index 0000000..fe7b6a9 --- /dev/null +++ b/src/lib/dateUtils.ts @@ -0,0 +1,54 @@ +import { isToday, isYesterday, format, parseISO, isPast } from "date-fns"; +import { zhCN, enUS } from "date-fns/locale"; +import i18n from "@/i18n"; + +export function getGroupLabel(dateString: string, currentLang?: string): string { + try { + const date = parseISO(dateString); + const isZh = (currentLang || i18n.language).startsWith("zh"); + const locale = isZh ? zhCN : enUS; + + if (isToday(date)) { + return i18n.t("today"); + } + if (isYesterday(date)) { + return i18n.t("yesterday"); + } + return format(date, isZh ? "yyyy年M月d日" : "MMM d, yyyy", { locale }); + } catch { + return i18n.t("earlier"); + } +} + +export function formatTime(dateString: string): string { + try { + const date = parseISO(dateString); + return format(date, "HH:mm"); + } catch { + return ""; + } +} + +export function formatDeadline( + dueAt: string, + currentLang?: string +): { label: string; isOverdue: boolean } { + try { + const date = parseISO(dueAt); + const overdue = isPast(date) && !isToday(date); + const isZh = (currentLang || i18n.language).startsWith("zh"); + const locale = isZh ? zhCN : enUS; + + let label = ""; + if (isToday(date)) { + label = `${i18n.t("today")} ${format(date, "HH:mm")}`; + } else if (isYesterday(date)) { + label = `${i18n.t("yesterday")} ${format(date, "HH:mm")}`; + } else { + label = format(date, isZh ? "M月d日 HH:mm" : "MMM d, HH:mm", { locale }); + } + return { label, isOverdue: overdue }; + } catch { + return { label: dueAt, isOverdue: false }; + } +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..75c7cf1 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import { App } from "./App"; +import "./index.css"; +import "./i18n"; + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + + + +); diff --git a/src/stores/useNoteStore.ts b/src/stores/useNoteStore.ts new file mode 100644 index 0000000..5c3c4e8 --- /dev/null +++ b/src/stores/useNoteStore.ts @@ -0,0 +1,125 @@ +import { create } from "zustand"; +import type { NoteItem } from "@/types"; +import { api } from "@/lib/api"; + +interface NoteState { + notes: NoteItem[]; + isLoaded: boolean; + searchQuery: string; + editingNoteId: string | null; + isEditorOpen: boolean; + editorMode: "view" | "edit"; + + setSearchQuery: (query: string) => void; + setEditingNoteId: (id: string | null) => void; + setIsEditorOpen: (val: boolean) => void; + setEditorMode: (mode: "view" | "edit") => void; + + loadNotes: () => Promise; + addNote: (title?: string, content?: string, tagIds?: string[]) => Promise; + updateNote: (id: string, updates: Partial) => Promise; + deleteNote: (id: string) => Promise; + togglePin: (id: string) => Promise; + toggleArchive: (id: string) => Promise; + removeTagFromNotes: (tagId: string) => Promise; +} + +export const useNoteStore = create((set, get) => ({ + notes: [], + isLoaded: false, + searchQuery: "", + editingNoteId: null, + isEditorOpen: false, + editorMode: "view", + + setSearchQuery: (query) => set({ searchQuery: query }), + setEditingNoteId: (id) => set({ editingNoteId: id }), + setIsEditorOpen: (val) => set({ isEditorOpen: val }), + setEditorMode: (mode) => set({ editorMode: mode }), + + loadNotes: async () => { + try { + const data = await api.getNotes(); + set({ notes: data, isLoaded: true }); + } catch { + set({ isLoaded: true }); + } + }, + + addNote: async (title = "", content = "", tagIds = []) => { + const now = new Date().toISOString(); + const newNote: NoteItem = { + id: crypto.randomUUID(), + title: title.trim(), + content: content.trim(), + createdAt: now, + updatedAt: now, + tagIds, + pinnedAt: null, + archivedAt: null, + }; + + const nextNotes = [newNote, ...get().notes]; + set({ notes: nextNotes, editingNoteId: newNote.id }); + await api.saveNotes(nextNotes); + return newNote; + }, + + updateNote: async (id: string, updates: Partial) => { + const now = new Date().toISOString(); + const nextNotes = get().notes.map((item) => + item.id === id ? { ...item, ...updates, updatedAt: now } : item + ); + set({ notes: nextNotes }); + await api.saveNotes(nextNotes); + }, + + deleteNote: async (id: string) => { + const nextNotes = get().notes.filter((item) => item.id !== id); + set({ + notes: nextNotes, + editingNoteId: get().editingNoteId === id ? null : get().editingNoteId, + }); + await api.saveNotes(nextNotes); + }, + + togglePin: async (id: string) => { + const now = new Date().toISOString(); + const nextNotes = get().notes.map((item) => { + if (item.id !== id) return item; + return { + ...item, + pinnedAt: item.pinnedAt ? null : now, + updatedAt: now, + }; + }); + set({ notes: nextNotes }); + await api.saveNotes(nextNotes); + }, + + toggleArchive: async (id: string) => { + const now = new Date().toISOString(); + const nextNotes = get().notes.map((item) => { + if (item.id !== id) return item; + return { + ...item, + archivedAt: item.archivedAt ? null : now, + pinnedAt: item.archivedAt ? item.pinnedAt : null, // unpin when archiving + updatedAt: now, + }; + }); + set({ notes: nextNotes }); + await api.saveNotes(nextNotes); + }, + + removeTagFromNotes: async (tagId: string) => { + const hasTag = get().notes.some((n) => n.tagIds.includes(tagId)); + if (!hasTag) return; + const nextNotes = get().notes.map((n) => ({ + ...n, + tagIds: n.tagIds.filter((tId) => tId !== tagId), + })); + set({ notes: nextNotes }); + await api.saveNotes(nextNotes); + }, +})); diff --git a/src/stores/useSettingsStore.ts b/src/stores/useSettingsStore.ts new file mode 100644 index 0000000..89f5183 --- /dev/null +++ b/src/stores/useSettingsStore.ts @@ -0,0 +1,123 @@ +import { create } from "zustand"; +import type { AppSettings, ThemePreference, LanguagePreference } from "@/types"; +import { api } from "@/lib/api"; +import i18n from "@/i18n"; + +interface SettingsState { + settings: AppSettings; + isLoaded: boolean; + loadSettings: () => Promise; + updateTheme: (theme: ThemePreference) => Promise; + updateLanguage: (language: LanguagePreference) => Promise; + updateAlwaysOnTop: (alwaysOnTop: boolean) => Promise; + updateCollapseOnBlur: (collapse: boolean) => Promise; +} + +const defaultSettings: AppSettings = { + theme: "system", + language: "system", + alwaysOnTop: true, + collapseWhenClickingOutside: true, +}; + +function applyTheme(theme: ThemePreference) { + const root = document.documentElement; + const isDark = + theme === "dark" || + (theme === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches); + + // Disable all CSS transitions during theme switch so all elements (header, search bar, buttons, list) + // switch 100% synchronously in a single atomic frame without any staggered delay or lag. + root.classList.add("disable-transitions"); + + if (isDark) { + root.classList.add("dark"); + root.classList.remove("light"); + } else { + root.classList.remove("dark"); + root.classList.add("light"); + } + + // Force reflow to immediately apply theme tokens while transitions are disabled + void root.offsetHeight; + + // Restore micro-interactions on the next frame for hover/focus states + requestAnimationFrame(() => { + requestAnimationFrame(() => { + root.classList.remove("disable-transitions"); + }); + }); +} + +// Reactively respond to OS light/dark changes when following system theme +if (typeof window !== "undefined" && window.matchMedia) { + const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + const handleSystemThemeChange = () => { + const currentTheme = useSettingsStore.getState().settings.theme; + if (currentTheme === "system") { + applyTheme("system"); + } + }; + if (mediaQuery.addEventListener) { + mediaQuery.addEventListener("change", handleSystemThemeChange); + } else { + // Legacy Safari/WebKit fallback + mediaQuery.addListener(handleSystemThemeChange); + } +} + +function applyLanguage(lang: LanguagePreference) { + if (lang === "system") { + const sysLang = navigator.language.toLowerCase().startsWith("zh") ? "zh" : "en"; + i18n.changeLanguage(sysLang); + } else { + i18n.changeLanguage(lang); + } +} + +export const useSettingsStore = create((set, get) => ({ + settings: defaultSettings, + isLoaded: false, + + loadSettings: async () => { + try { + const data = await api.getSettings(); + const current = { ...defaultSettings, ...data }; + set({ settings: current, isLoaded: true }); + applyTheme(current.theme); + applyLanguage(current.language); + await api.setAlwaysOnTop(current.alwaysOnTop); + } catch { + set({ isLoaded: true }); + applyTheme(defaultSettings.theme); + applyLanguage(defaultSettings.language); + } + }, + + updateTheme: async (theme: ThemePreference) => { + const next = { ...get().settings, theme }; + set({ settings: next }); + applyTheme(theme); + await api.saveSettings(next); + }, + + updateLanguage: async (language: LanguagePreference) => { + const next = { ...get().settings, language }; + set({ settings: next }); + applyLanguage(language); + await api.saveSettings(next); + }, + + updateAlwaysOnTop: async (alwaysOnTop: boolean) => { + const next = { ...get().settings, alwaysOnTop }; + set({ settings: next }); + await api.setAlwaysOnTop(alwaysOnTop); + await api.saveSettings(next); + }, + + updateCollapseOnBlur: async (collapseWhenClickingOutside: boolean) => { + const next = { ...get().settings, collapseWhenClickingOutside }; + set({ settings: next }); + await api.saveSettings(next); + }, +})); diff --git a/src/stores/useTagStore.ts b/src/stores/useTagStore.ts new file mode 100644 index 0000000..93cda75 --- /dev/null +++ b/src/stores/useTagStore.ts @@ -0,0 +1,173 @@ +import { create } from "zustand"; +import type { TagWorkspace, TodoTag } from "@/types"; +import { api } from "@/lib/api"; +import { useNoteStore } from "./useNoteStore"; + +interface TagState { + workspace: TagWorkspace; + isLoaded: boolean; + selectedTagIds: string[]; + + toggleTagFilter: (tagId: string) => void; + clearTagFilter: () => void; + setSelectedTagIds: (tagIds: string[]) => void; + + loadTags: () => Promise; + createTag: (name: string, colorHex: string) => Promise; + updateTag: (id: string, name: string, colorHex: string) => Promise; + deleteTag: (id: string) => Promise; + setTodoTags: (todoId: string, tagIds: string[]) => Promise; + toggleTodoTag: (todoId: string, tagId: string) => Promise; + removeAssignmentForTodo: (todoId: string) => Promise; +} + +const defaultWorkspace: TagWorkspace = { + version: 1, + tags: [], + assignments: {}, +}; + +export const useTagStore = create((set, get) => ({ + workspace: defaultWorkspace, + isLoaded: false, + selectedTagIds: [], + + toggleTagFilter: (tagId: string) => { + const current = get().selectedTagIds; + if (current.includes(tagId)) { + set({ selectedTagIds: current.filter((id) => id !== tagId) }); + } else { + set({ selectedTagIds: [...current, tagId] }); + } + }, + + clearTagFilter: () => { + set({ selectedTagIds: [] }); + }, + + setSelectedTagIds: (tagIds: string[]) => { + set({ selectedTagIds: tagIds }); + }, + + loadTags: async () => { + try { + const data = await api.getTags(); + set({ workspace: data, isLoaded: true }); + } catch { + set({ isLoaded: true }); + } + }, + + createTag: async (name: string, colorHex: string) => { + const trimmed = name.trim(); + if (!trimmed) { + throw new Error("Tag name cannot be empty"); + } + if (trimmed.length > 30) { + throw new Error("Tag name exceeds 30 characters"); + } + const lower = trimmed.toLowerCase(); + if (get().workspace.tags.some((t) => t.name.toLowerCase() === lower)) { + throw new Error("Tag name already exists"); + } + + const newTag: TodoTag = { + id: crypto.randomUUID(), + name: trimmed, + colorHex, + }; + const nextWorkspace: TagWorkspace = { + ...get().workspace, + tags: [...get().workspace.tags, newTag], + }; + set({ workspace: nextWorkspace }); + await api.saveTags(nextWorkspace); + return newTag; + }, + + updateTag: async (id: string, name: string, colorHex: string) => { + const trimmed = name.trim(); + if (!trimmed) { + throw new Error("Tag name cannot be empty"); + } + if (trimmed.length > 30) { + throw new Error("Tag name exceeds 30 characters"); + } + const lower = trimmed.toLowerCase(); + if ( + get().workspace.tags.some( + (t) => t.id !== id && t.name.toLowerCase() === lower + ) + ) { + throw new Error("Tag name already exists"); + } + + const nextWorkspace: TagWorkspace = { + ...get().workspace, + tags: get().workspace.tags.map((t) => + t.id === id ? { ...t, name: trimmed, colorHex } : t + ), + }; + set({ workspace: nextWorkspace }); + await api.saveTags(nextWorkspace); + }, + + deleteTag: async (id: string) => { + // 1. Clean up assignments for todos + const nextAssignments = { ...get().workspace.assignments }; + for (const key of Object.keys(nextAssignments)) { + nextAssignments[key] = nextAssignments[key].filter((tId) => tId !== id); + } + const nextWorkspace: TagWorkspace = { + version: 1, + tags: get().workspace.tags.filter((t) => t.id !== id), + assignments: nextAssignments, + }; + + // 2. Remove from active filter + set({ + workspace: nextWorkspace, + selectedTagIds: get().selectedTagIds.filter((tId) => tId !== id), + }); + + // 3. Save tags workspace + await api.saveTags(nextWorkspace); + + // 4. Clean up tag from all notes + await useNoteStore.getState().removeTagFromNotes(id); + }, + + setTodoTags: async (todoId: string, tagIds: string[]) => { + const nextAssignments = { + ...get().workspace.assignments, + [todoId]: Array.from(new Set(tagIds)), + }; + const nextWorkspace: TagWorkspace = { + ...get().workspace, + assignments: nextAssignments, + }; + set({ workspace: nextWorkspace }); + await api.saveTags(nextWorkspace); + }, + + toggleTodoTag: async (todoId: string, tagId: string) => { + const current = get().workspace.assignments[todoId] || []; + const nextTagIds = current.includes(tagId) + ? current.filter((id) => id !== tagId) + : [...current, tagId]; + + await get().setTodoTags(todoId, nextTagIds); + }, + + removeAssignmentForTodo: async (todoId: string) => { + if (!get().workspace.assignments[todoId]) return; + const nextAssignments = { ...get().workspace.assignments }; + delete nextAssignments[todoId]; + const nextWorkspace: TagWorkspace = { + ...get().workspace, + assignments: nextAssignments, + }; + set({ workspace: nextWorkspace }); + await api.saveTags(nextWorkspace); + }, +})); diff --git a/src/stores/useTodoStore.ts b/src/stores/useTodoStore.ts new file mode 100644 index 0000000..a07668d --- /dev/null +++ b/src/stores/useTodoStore.ts @@ -0,0 +1,123 @@ +import { create } from "zustand"; +import type { TodoItem } from "@/types"; +import { api } from "@/lib/api"; +import { useTagStore } from "./useTagStore"; + +interface TodoState { + todos: TodoItem[]; + isLoaded: boolean; + searchQuery: string; + activeScope: "active" | "archived"; + editingTodoId: string | null; + isEditorOpen: boolean; + editorMode: "view" | "edit"; + + setSearchQuery: (query: string) => void; + setActiveScope: (scope: "active" | "archived") => void; + setEditingTodoId: (id: string | null) => void; + setIsEditorOpen: (val: boolean) => void; + setEditorMode: (mode: "view" | "edit") => void; + + loadTodos: () => Promise; + addTodo: (title: string, content?: string, tagId?: string) => Promise; + toggleComplete: (id: string) => Promise; + updateTodo: (id: string, updates: Partial) => Promise; + deleteTodo: (id: string) => Promise; + archiveTodo: (id: string) => Promise; + restoreTodo: (id: string) => Promise; +} + +export const useTodoStore = create((set, get) => ({ + todos: [], + isLoaded: false, + searchQuery: "", + activeScope: "active", + editingTodoId: null, + isEditorOpen: false, + editorMode: "view", + + setSearchQuery: (query) => set({ searchQuery: query }), + setActiveScope: (scope) => set({ activeScope: scope }), + setEditingTodoId: (id) => set({ editingTodoId: id }), + setIsEditorOpen: (val) => set({ isEditorOpen: val }), + setEditorMode: (mode) => set({ editorMode: mode }), + + loadTodos: async () => { + try { + const data = await api.getTodos(); + set({ todos: data, isLoaded: true }); + } catch { + set({ isLoaded: true }); + } + }, + + addTodo: async (title: string, content: string = "") => { + const trimmed = title.trim(); + if (!trimmed) throw new Error("Title cannot be empty"); + + const newTodo: TodoItem = { + id: crypto.randomUUID(), + title: trimmed, + content: content.trim(), + createdAt: new Date().toISOString(), + startedAt: null, + completedAt: null, + archivedAt: null, + dueAt: null, + reminderAt: null, + notifyAtDeadline: true, + }; + + const nextTodos = [newTodo, ...get().todos]; + set({ todos: nextTodos }); + await api.saveTodos(nextTodos); + return newTodo; + }, + + toggleComplete: async (id: string) => { + const now = new Date().toISOString(); + const nextTodos = get().todos.map((item) => { + if (item.id !== id) return item; + const isCompleted = !!item.completedAt; + return { + ...item, + completedAt: isCompleted ? null : now, + }; + }); + set({ todos: nextTodos }); + await api.saveTodos(nextTodos); + }, + + updateTodo: async (id: string, updates: Partial) => { + const nextTodos = get().todos.map((item) => + item.id === id ? { ...item, ...updates } : item + ); + set({ todos: nextTodos }); + await api.saveTodos(nextTodos); + }, + + deleteTodo: async (id: string) => { + const nextTodos = get().todos.filter((item) => item.id !== id); + set({ todos: nextTodos }); + await api.saveTodos(nextTodos); + // Clean up tag assignments for this deleted todo + await useTagStore.getState().removeAssignmentForTodo(id); + }, + + archiveTodo: async (id: string) => { + const now = new Date().toISOString(); + const nextTodos = get().todos.map((item) => + item.id === id ? { ...item, archivedAt: now, startedAt: null } : item + ); + set({ todos: nextTodos }); + await api.saveTodos(nextTodos); + }, + + restoreTodo: async (id: string) => { + const nextTodos = get().todos.map((item) => + item.id === id ? { ...item, archivedAt: null } : item + ); + set({ todos: nextTodos }); + await api.saveTodos(nextTodos); + }, +})); diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..af892dd --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,50 @@ +export interface TodoItem { + id: string; + title: string; + content?: string; + createdAt: string; // ISO-8601 + startedAt?: string | null; + completedAt?: string | null; + archivedAt?: string | null; + dueAt?: string | null; + reminderAt?: string | null; + notifyAtDeadline?: boolean; + deadlineNotifiedAt?: string | null; + reminderNotifiedAt?: string | null; + snoozedUntil?: string | null; +} + +export interface TodoTag { + id: string; + name: string; + colorHex: string; + colorValue?: number; + createdAt?: string; +} + +export interface TagWorkspace { + version: number; + tags: TodoTag[]; + assignments: Record; // todoId -> tagIds +} + +export interface NoteItem { + id: string; + title: string; + content: string; + createdAt: string; // ISO-8601 + updatedAt: string; // ISO-8601 + tagIds: string[]; + pinnedAt?: string | null; + archivedAt?: string | null; +} + +export type ThemePreference = 'system' | 'light' | 'dark'; +export type LanguagePreference = 'system' | 'zh' | 'en'; + +export interface AppSettings { + theme: ThemePreference; + language: LanguagePreference; + alwaysOnTop: boolean; + collapseWhenClickingOutside: boolean; +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0b9a6c6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": false, + "resolveJsonModule": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + /* Path alias */ + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..0540bc4 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; +import path from "path"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], + resolve: { + alias: { + "@": path.resolve(import.meta.dirname, "./src"), + }, + }, + // Vite options tailored for Tauri development + clearScreen: false, + server: { + port: 1420, + strictPort: true, + watch: { + ignored: ["**/src-tauri/**"], + }, + }, +}); diff --git a/website/README.md b/website/README.md index 13cbac8..edb2598 100644 --- a/website/README.md +++ b/website/README.md @@ -13,6 +13,25 @@ npm run dev Astro prints the local preview URL after startup. English is served at `/` and Simplified Chinese at `/zh/`. +## Product preview and downloads + +The homepage previews the upcoming menu bar app. The current public download is +v0.3.4. `src/config/site-links.ts` keeps the public version, pinned download URL, +and development preview together. Update that file when publishing a release, +then update the preview notices and reminder details in `site-copy.ts` and +`changelog.ts`. Do not label a development build as the latest published release. + +The six `public/images/product-{todos,notes,editor}-{en,zh}.png` images are 2× +captures of the real React interface at 440 × 700 CSS pixels, populated with +fictional everyday tasks. Capture in the browser mock mode, never from a user's +local workspace. The showcase supports clicks, arrow keys, Home/End, and a +JavaScript-free fallback. + +The menu bar uses Floatick's actual template tray icon and native SF Symbols +exported with AppKit. Regenerate the four system symbols on macOS with +`xcrun swift scripts/export-menubar-symbols.swift public/images/menubar`. +Its sample date and time match the product captures; it is a static preview. + ## Production build ```bash @@ -24,8 +43,9 @@ The static output is written to `dist/`. ## Cloudflare Pages -Deployment is intentionally deferred while the website is developed locally. -When enabled, configure Pages with: +Cloudflare Pages is connected to this repository. Pushes to `main` deploy the +production website; development branches receive preview deployments. GitHub +reports the result through the `Cloudflare Pages` check. The project uses: ```text Root directory: website diff --git a/website/pnpm-lock.yaml b/website/pnpm-lock.yaml new file mode 100644 index 0000000..2fb94a0 --- /dev/null +++ b/website/pnpm-lock.yaml @@ -0,0 +1,2695 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@astrojs/sitemap': + specifier: ^3.7.3 + version: 3.7.4 + '@fontsource-variable/manrope': + specifier: ^5.3.0 + version: 5.3.0 + astro: + specifier: ^7.1.6 + version: 7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.13.3) + three: + specifier: ^0.185.1 + version: 0.185.1 + devDependencies: + '@types/three': + specifier: ^0.185.1 + version: 0.185.4 + +packages: + + '@astrojs/compiler-binding-darwin-arm64@0.4.0': + resolution: {integrity: sha512-ZVUwHundaQyFNjE6uoa0usaC0WOCitDCLS/4mdb4rOiJXwVUuKJBMxI5WMzXLWmamsXtK/Z//ifLXvV5Yeh4Hw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@astrojs/compiler-binding-darwin-x64@0.4.0': + resolution: {integrity: sha512-FI6G8AY8u6fR1SI/QRR5yGMwtvZwP34CDmZpZ5HwJGa50UM1VISTLhqkhV4a476pmgd25X1Aur2dqw6hUnrlKA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@astrojs/compiler-binding-linux-arm64-gnu@0.4.0': + resolution: {integrity: sha512-lB9gLFJK7m82EnjaU8nlRBEfcwGNeHidW3sSjODTUjMNaoewVuUz9fwwdY5M4jiSXIqWLH3yl6TX8FTDKA74Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@astrojs/compiler-binding-linux-arm64-musl@0.4.0': + resolution: {integrity: sha512-HPbvWqbxFxyaoQJhLxCaSjtYBx9KBo7JGVzEFZCmMl968a2PsSH0UfiODYgYPXofTOIsIH2aoCcrHXML0IA3ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@astrojs/compiler-binding-linux-x64-gnu@0.4.0': + resolution: {integrity: sha512-tQKolMxoJ/+0AmLWm1PmJ/i+z3i10ZU1bNuVjEDulCf48azEMtUNjTZgHJ5MPtpYRNc7dlETr8QujUfduzoC7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@astrojs/compiler-binding-linux-x64-musl@0.4.0': + resolution: {integrity: sha512-5v5YymudsxMHp3NBLCS8BUlu5CRqeLtWD9cKS/4nIhIEHCbpz9okmVV6I0HWqmBAPhWYcDa3vw/vltYPrOQCTA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@astrojs/compiler-binding-wasm32-wasi@0.4.0': + resolution: {integrity: sha512-m/phuH3x3PREvv1OnkM44NoPh4MatUadix1fB1u5SvMLCyDTUZykDJbKnWf1cjnYmHdlB8HcjTjl6JrCqAIXcw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@astrojs/compiler-binding-win32-arm64-msvc@0.4.0': + resolution: {integrity: sha512-B9zYf3okEY83kM8gydlpH2BHP00w4ifxPqlYlWrgTwuD6wnkrJDCwBlgy1q31cERjCJRXN1lrE2VmkLvFjv/6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@astrojs/compiler-binding-win32-x64-msvc@0.4.0': + resolution: {integrity: sha512-zB0Nrv0dGc0zZWPGDRmmETTPhDRqyZjAjk+gWMlVrJX5U89obpB3VUUE1ZiHxOCN5LQojeLK6O8L/dnoHolvNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@astrojs/compiler-binding@0.4.0': + resolution: {integrity: sha512-x2RjDUuWfwLNtc3mjAdSRInwqh/rqbLar9cm/5FOMbHvmYZB7yfKewzSclAxWjIZsypJDXv1lhaP2WG+P8TK3g==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@astrojs/compiler-rs@0.4.0': + resolution: {integrity: sha512-koVikeon1kreEy+/JzLQRy3vzHHQVOjycs4degg4vFufKApZOwMZvSSAEztYNhmcQVfNVsVZZI4cEge3cexAbQ==} + engines: {node: '>=22.12.0'} + + '@astrojs/internal-helpers@0.11.0': + resolution: {integrity: sha512-3rzxJ+xbo0+8YyqOzLziIN32wmsHdCjEVz2sGOpRxJ+Ben/KiLph4ItxBy1abEL+E8fkRzqjg0rfXmaHJGw9JA==} + + '@astrojs/markdown-satteri@0.4.0': + resolution: {integrity: sha512-wykOOW9KsUVcZweOpY/CeXpdKcCKZy6fQbdcteWFuI75+sQCiqxYM7VKsGa5b+aGl3cYQscFY37rsbbyal5MRw==} + + '@astrojs/prism@4.0.2': + resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==} + engines: {node: '>=22.12.0'} + + '@astrojs/sitemap@3.7.4': + resolution: {integrity: sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA==} + + '@astrojs/telemetry@3.3.3': + resolution: {integrity: sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@bruits/satteri-darwin-arm64@0.10.5': + resolution: {integrity: sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==} + cpu: [arm64] + os: [darwin] + + '@bruits/satteri-darwin-x64@0.10.5': + resolution: {integrity: sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==} + cpu: [x64] + os: [darwin] + + '@bruits/satteri-linux-arm64-gnu@0.10.5': + resolution: {integrity: sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@bruits/satteri-linux-arm64-musl@0.10.5': + resolution: {integrity: sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@bruits/satteri-linux-x64-gnu@0.10.5': + resolution: {integrity: sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@bruits/satteri-linux-x64-musl@0.10.5': + resolution: {integrity: sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@bruits/satteri-wasm32-wasi@0.10.5': + resolution: {integrity: sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@bruits/satteri-win32-arm64-msvc@0.10.5': + resolution: {integrity: sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==} + cpu: [arm64] + os: [win32] + + '@bruits/satteri-win32-x64-msvc@0.10.5': + resolution: {integrity: sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==} + cpu: [x64] + os: [win32] + + '@capsizecss/unpack@4.0.1': + resolution: {integrity: sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==} + engines: {node: '>=18'} + + '@clack/core@1.4.3': + resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.7.0': + resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} + engines: {node: '>= 20.12.0'} + + '@dimforge/rapier3d-compat@0.12.0': + resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@fontsource-variable/manrope@5.3.0': + resolution: {integrity: sha512-6D5dgokHsWDDMtmXHznKa0hK229NN+1a4BLPmUCLqcO1Pw5EEhWY5RFt0AcXnVRAljFFPfRtLkJePQj6LSsV6g==} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + + '@oxc-project/types@0.148.0': + resolution: {integrity: sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==} + + '@rolldown/binding-android-arm-eabi@1.2.7': + resolution: {integrity: sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.7': + resolution: {integrity: sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.7': + resolution: {integrity: sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.7': + resolution: {integrity: sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.7': + resolution: {integrity: sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + resolution: {integrity: sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.7': + resolution: {integrity: sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.7': + resolution: {integrity: sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.7': + resolution: {integrity: sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.7': + resolution: {integrity: sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.7': + resolution: {integrity: sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.7': + resolution: {integrity: sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.7': + resolution: {integrity: sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.7': + resolution: {integrity: sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.7': + resolution: {integrity: sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@shikijs/core@4.4.3': + resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.4.3': + resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.4.3': + resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} + engines: {node: '>=20'} + + '@shikijs/langs@4.4.3': + resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.4.3': + resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} + engines: {node: '>=20'} + + '@shikijs/themes@4.4.3': + resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} + engines: {node: '>=20'} + + '@shikijs/types@4.4.3': + resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/stats.js@0.17.4': + resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==} + + '@types/three@0.185.4': + resolution: {integrity: sha512-gAsBIC07NIFrxjbf7tH2t71c38uulFfk/RFoC7FNBSjMRAQ8J1x/RBvusX0N5PJouaYFJawXQqfCQ0RKUx/1nA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/webxr@0.5.24': + resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==} + + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + + am-i-vibing@0.4.0: + resolution: {integrity: sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==} + hasBin: true + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + astro@7.2.10: + resolution: {integrity: sha512-uPtD+nK6kQXugyq4kiMPwj9OI3Sae6HSerA5X+fuv2CPaDwxmokFPPFlGRKIAXH0QAKu+zot2q6yrLG61wFmqg==} + engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + peerDependencies: + '@astrojs/markdown-remark': ^7.3.0 + peerDependenciesMeta: + '@astrojs/markdown-remark': + optional: true + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + common-ancestor-path@2.0.0: + resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} + engines: {node: '>= 18'} + + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + + cookie@2.0.1: + resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==} + engines: {node: '>=22'} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + css-select@6.0.0: + resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@7.0.0: + resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} + engines: {node: '>= 6'} + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.9.2: + resolution: {integrity: sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} + engines: {node: '>=0.3.1'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + find-proc@0.1.0: + resolution: {integrity: sha512-OaOpEYv2PiQ7SQ5LIrl+deA1XaWcxEjnpM6VuWXTUvn+teIXxeFTLDmu18/zDQpFmHN4o3oDBX+BT0AGwEhemg==} + engines: {node: ^20.19.0 || >=22.12.0} + + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + + fontace@0.4.1: + resolution: {integrity: sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==} + + fontkitten@1.0.3: + resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} + engines: {node: '>=20'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@5.0.0-beta.4: + resolution: {integrity: sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==} + engines: {node: '>=20.20.0'} + + github-slugger@2.0.0: + resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + + h3@1.15.11: + resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-docker@4.0.0: + resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} + engines: {node: '>=20'} + hasBin: true + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} + + magic-string@1.2.3: + resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} + + magicast@0.5.4: + resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + meshoptimizer@1.1.1: + resolution: {integrity: sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + neotraverse@1.0.1: + resolution: {integrity: sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==} + engines: {node: '>= 10'} + + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-mock-http@1.0.5: + resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.12: + resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + p-limit@7.3.2: + resolution: {integrity: sha512-Ll0w3fU24vYpXoZmjjZIee6bJQDgG0oAyo1PdmFYI8UDwJJddaHAypxIH9avUu+t+lSsAwKVsb1jDCMIIChliw==} + engines: {node: '>=20'} + + p-queue@9.3.3: + resolution: {integrity: sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==} + engines: {node: '>=20'} + + p-timeout@7.0.1: + resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + engines: {node: '>=20'} + + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + + piccolore@0.1.3: + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss@8.5.27: + resolution: {integrity: sha512-79Iho8QeYyooJ8e9lCRyTVlyTAkS/kXBYKff6TMzS3kEWGQ8Ds5UEtXpGrSUDLUWok6QTvxeYy0GO8fopHnaSA==} + engines: {node: ^10 || ^12 || >=14} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + process-ancestry@0.1.0: + resolution: {integrity: sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==} + engines: {node: '>=18.0.0'} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + rolldown@1.2.7: + resolution: {integrity: sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + satteri@0.10.5: + resolution: {integrity: sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==} + + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} + engines: {node: '>=11.0.0'} + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + shiki@4.4.3: + resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} + engines: {node: '>=20'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + sitemap@9.0.1: + resolution: {integrity: sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==} + engines: {node: '>=20.19.5', npm: '>=10.8.2'} + hasBin: true + + smol-toml@1.8.0: + resolution: {integrity: sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + svgo@4.1.0: + resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} + engines: {node: '>=16'} + hasBin: true + + three@0.185.1: + resolution: {integrity: sha512-5aojFCXKwnjBRZvUnt3WFfEcvUJgkN5LlijRFN95hMy8WVkG4I0QNcJE+OuWvuJ0bOdStrbfXn0pkd6/QyiAlg==} + + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + + tinyclip@0.1.15: + resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} + engines: {node: ^16.14.0 || >= 17.3.0} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + ultrahtml@1.7.0: + resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + undici@8.10.1: + resolution: {integrity: sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==} + engines: {node: '>=22.19.0'} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unifont@0.7.5: + resolution: {integrity: sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + unstorage@1.17.5: + resolution: {integrity: sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6 || ^7 || ^8 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1 || ^2 || ^3 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@8.2.2: + resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 || ^0.5.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + zod@4.5.4: + resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@astrojs/compiler-binding-darwin-arm64@0.4.0': + optional: true + + '@astrojs/compiler-binding-darwin-x64@0.4.0': + optional: true + + '@astrojs/compiler-binding-linux-arm64-gnu@0.4.0': + optional: true + + '@astrojs/compiler-binding-linux-arm64-musl@0.4.0': + optional: true + + '@astrojs/compiler-binding-linux-x64-gnu@0.4.0': + optional: true + + '@astrojs/compiler-binding-linux-x64-musl@0.4.0': + optional: true + + '@astrojs/compiler-binding-wasm32-wasi@0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@astrojs/compiler-binding-win32-arm64-msvc@0.4.0': + optional: true + + '@astrojs/compiler-binding-win32-x64-msvc@0.4.0': + optional: true + + '@astrojs/compiler-binding@0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + optionalDependencies: + '@astrojs/compiler-binding-darwin-arm64': 0.4.0 + '@astrojs/compiler-binding-darwin-x64': 0.4.0 + '@astrojs/compiler-binding-linux-arm64-gnu': 0.4.0 + '@astrojs/compiler-binding-linux-arm64-musl': 0.4.0 + '@astrojs/compiler-binding-linux-x64-gnu': 0.4.0 + '@astrojs/compiler-binding-linux-x64-musl': 0.4.0 + '@astrojs/compiler-binding-wasm32-wasi': 0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + '@astrojs/compiler-binding-win32-arm64-msvc': 0.4.0 + '@astrojs/compiler-binding-win32-x64-msvc': 0.4.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@astrojs/compiler-rs@0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + dependencies: + '@astrojs/compiler-binding': 0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@astrojs/internal-helpers@0.11.0': + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + js-yaml: 4.3.2 + picomatch: 4.0.7 + retext-smartypants: 6.2.0 + shiki: 4.4.3 + smol-toml: 1.8.0 + unified: 11.0.5 + + '@astrojs/markdown-satteri@0.4.0': + dependencies: + '@astrojs/internal-helpers': 0.11.0 + '@astrojs/prism': 4.0.2 + github-slugger: 2.0.0 + satteri: 0.10.5 + + '@astrojs/prism@4.0.2': + dependencies: + prismjs: 1.30.0 + + '@astrojs/sitemap@3.7.4': + dependencies: + sitemap: 9.0.1 + zod: 4.5.4 + + '@astrojs/telemetry@3.3.3': + dependencies: + ci-info: 4.4.0 + dset: 3.1.4 + is-docker: 4.0.0 + package-manager-detector: 1.8.0 + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bruits/satteri-darwin-arm64@0.10.5': + optional: true + + '@bruits/satteri-darwin-x64@0.10.5': + optional: true + + '@bruits/satteri-linux-arm64-gnu@0.10.5': + optional: true + + '@bruits/satteri-linux-arm64-musl@0.10.5': + optional: true + + '@bruits/satteri-linux-x64-gnu@0.10.5': + optional: true + + '@bruits/satteri-linux-x64-musl@0.10.5': + optional: true + + '@bruits/satteri-wasm32-wasi@0.10.5': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@bruits/satteri-win32-arm64-msvc@0.10.5': + optional: true + + '@bruits/satteri-win32-x64-msvc@0.10.5': + optional: true + + '@capsizecss/unpack@4.0.1': + dependencies: + fontkitten: 1.0.3 + + '@clack/core@1.4.3': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.7.0': + dependencies: + '@clack/core': 1.4.3 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@dimforge/rapier3d-compat@0.12.0': {} + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.2': + optional: true + + '@esbuild/android-arm64@0.28.2': + optional: true + + '@esbuild/android-arm@0.28.2': + optional: true + + '@esbuild/android-x64@0.28.2': + optional: true + + '@esbuild/darwin-arm64@0.28.2': + optional: true + + '@esbuild/darwin-x64@0.28.2': + optional: true + + '@esbuild/freebsd-arm64@0.28.2': + optional: true + + '@esbuild/freebsd-x64@0.28.2': + optional: true + + '@esbuild/linux-arm64@0.28.2': + optional: true + + '@esbuild/linux-arm@0.28.2': + optional: true + + '@esbuild/linux-ia32@0.28.2': + optional: true + + '@esbuild/linux-loong64@0.28.2': + optional: true + + '@esbuild/linux-mips64el@0.28.2': + optional: true + + '@esbuild/linux-ppc64@0.28.2': + optional: true + + '@esbuild/linux-riscv64@0.28.2': + optional: true + + '@esbuild/linux-s390x@0.28.2': + optional: true + + '@esbuild/linux-x64@0.28.2': + optional: true + + '@esbuild/netbsd-arm64@0.28.2': + optional: true + + '@esbuild/netbsd-x64@0.28.2': + optional: true + + '@esbuild/openbsd-arm64@0.28.2': + optional: true + + '@esbuild/openbsd-x64@0.28.2': + optional: true + + '@esbuild/openharmony-arm64@0.28.2': + optional: true + + '@esbuild/sunos-x64@0.28.2': + optional: true + + '@esbuild/win32-arm64@0.28.2': + optional: true + + '@esbuild/win32-ia32@0.28.2': + optional: true + + '@esbuild/win32-x64@0.28.2': + optional: true + + '@fontsource-variable/manrope@5.3.0': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.3 + optional: true + + '@img/sharp-darwin-x64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.3 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.4': + dependencies: + '@img/sharp-wasm32': 0.35.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + optional: true + + '@img/sharp-linux-arm64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.3 + optional: true + + '@img/sharp-linux-arm@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.3 + optional: true + + '@img/sharp-linux-ppc64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.3 + optional: true + + '@img/sharp-linux-riscv64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.3 + optional: true + + '@img/sharp-linux-s390x@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.3 + optional: true + + '@img/sharp-linux-x64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + optional: true + + '@img/sharp-wasm32@0.35.4': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.4': + dependencies: + '@img/sharp-wasm32': 0.35.4 + optional: true + + '@img/sharp-win32-arm64@0.35.4': + optional: true + + '@img/sharp-win32-ia32@0.35.4': + optional: true + + '@img/sharp-win32-x64@0.35.4': + optional: true + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@oslojs/encoding@1.1.0': {} + + '@oxc-project/types@0.148.0': {} + + '@rolldown/binding-android-arm-eabi@1.2.7': + optional: true + + '@rolldown/binding-android-arm64@1.2.7': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.7': + optional: true + + '@rolldown/binding-darwin-x64@1.2.7': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.7': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.7': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.7': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.7': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.7': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.7': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.7': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.7': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@shikijs/core@4.4.3': + dependencies: + '@shikijs/primitive': 4.4.3 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + + '@shikijs/primitive@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/themes@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + + '@shikijs/types@4.4.3': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@tweenjs/tween.js@23.1.3': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@24.13.3': + dependencies: + undici-types: 7.18.2 + + '@types/sax@1.2.7': + dependencies: + '@types/node': 24.13.3 + + '@types/stats.js@0.17.4': {} + + '@types/three@0.185.4': + dependencies: + '@dimforge/rapier3d-compat': 0.12.0 + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.4 + '@types/webxr': 0.5.24 + fflate: 0.8.3 + meshoptimizer: 1.1.1 + + '@types/unist@3.0.3': {} + + '@types/webxr@0.5.24': {} + + '@ungap/structured-clone@1.4.0': {} + + am-i-vibing@0.4.0: + dependencies: + process-ancestry: 0.1.0 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + astro@7.2.10(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@24.13.3): + dependencies: + '@astrojs/compiler-rs': 0.4.0(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3) + '@astrojs/internal-helpers': 0.11.0 + '@astrojs/markdown-satteri': 0.4.0 + '@astrojs/telemetry': 3.3.3 + '@capsizecss/unpack': 4.0.1 + '@clack/prompts': 1.7.0 + '@oslojs/encoding': 1.1.0 + am-i-vibing: 0.4.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + ci-info: 4.4.0 + clsx: 2.1.1 + common-ancestor-path: 2.0.0 + cookie: 2.0.1 + devalue: 5.9.2 + diff: 9.0.0 + dset: 3.1.4 + es-module-lexer: 2.3.2 + esbuild: 0.28.2 + find-proc: 0.1.0 + flattie: 1.1.1 + fontace: 0.4.1 + get-tsconfig: 5.0.0-beta.4 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + js-yaml: 4.3.2 + jsonc-parser: 3.3.1 + magic-string: 1.2.3 + magicast: 0.5.4 + mrmime: 2.0.1 + neotraverse: 1.0.1 + obug: 2.1.4 + p-limit: 7.3.2 + p-queue: 9.3.3 + package-manager-detector: 1.8.0 + piccolore: 0.1.3 + picomatch: 4.0.7 + semver: 7.8.5 + shiki: 4.4.3 + smol-toml: 1.8.0 + svgo: 4.1.0 + tinyclip: 0.1.15 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + ultrahtml: 1.7.0 + unifont: 0.7.5 + unstorage: 1.17.5 + vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2) + vitefu: 1.1.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)) + xxhash-wasm: 1.1.0 + yargs-parser: 22.0.0 + zod: 4.5.4 + optionalDependencies: + sharp: 0.35.4(@types/node@24.13.3) + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@emnapi/core' + - '@emnapi/runtime' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - '@vitejs/devtools' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - uploadthing + - yaml + + axobject-query@4.1.0: {} + + bail@2.0.2: {} + + boolbase@1.0.0: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + + ci-info@4.4.0: {} + + clsx@2.1.1: {} + + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} + + common-ancestor-path@2.0.0: {} + + cookie-es@1.2.3: {} + + cookie@2.0.1: {} + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + css-select@6.0.0: + dependencies: + boolbase: 1.0.0 + css-what: 7.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css-what@7.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + defu@6.1.7: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-libc@2.1.2: {} + + devalue@5.9.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@9.0.0: {} + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dset@3.1.4: {} + + entities@4.5.0: {} + + es-module-lexer@2.3.2: {} + + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 + + eventemitter3@5.0.4: {} + + extend@3.0.2: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fflate@0.8.3: {} + + find-proc@0.1.0: {} + + flattie@1.1.1: {} + + fontace@0.4.1: + dependencies: + fontkitten: 1.0.3 + + fontkitten@1.0.3: + dependencies: + tiny-inflate: 1.0.3 + + fsevents@2.3.3: + optional: true + + get-tsconfig@5.0.0-beta.4: + dependencies: + resolve-pkg-maps: 1.0.0 + + github-slugger@2.0.0: {} + + h3@1.15.11: + dependencies: + cookie-es: 1.2.3 + crossws: 0.3.5 + defu: 6.1.7 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.5 + radix3: 1.1.2 + ufo: 1.6.4 + uncrypto: 0.1.3 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + http-cache-semantics@4.2.0: {} + + iron-webcrypto@1.2.1: {} + + is-docker@4.0.0: {} + + is-plain-obj@4.1.0: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsonc-parser@3.3.1: {} + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lru-cache@11.5.2: {} + + magic-string@1.2.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.4: + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + source-map-js: 1.2.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdn-data@2.0.28: {} + + mdn-data@2.27.1: {} + + meshoptimizer@1.1.1: {} + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + mrmime@2.0.1: {} + + nanoid@3.3.18: {} + + neotraverse@1.0.1: {} + + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + + node-fetch-native@1.6.7: {} + + node-mock-http@1.0.5: {} + + normalize-path@3.0.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + obug@2.1.4: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + ohash@2.0.12: {} + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + p-limit@7.3.2: + dependencies: + yocto-queue: 1.2.2 + + p-queue@9.3.3: + dependencies: + eventemitter3: 5.0.4 + p-timeout: 7.0.1 + + p-timeout@7.0.1: {} + + package-manager-detector@1.8.0: {} + + piccolore@0.1.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.7: {} + + postcss@8.5.27: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prismjs@1.30.0: {} + + process-ancestry@0.1.0: {} + + property-information@7.2.0: {} + + radix3@1.1.2: {} + + readdirp@5.1.1: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + resolve-pkg-maps@1.0.0: {} + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.1.0 + + rolldown@1.2.7: + dependencies: + '@oxc-project/types': 0.148.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.7 + '@rolldown/binding-android-arm64': 1.2.7 + '@rolldown/binding-darwin-arm64': 1.2.7 + '@rolldown/binding-darwin-x64': 1.2.7 + '@rolldown/binding-freebsd-x64': 1.2.7 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.7 + '@rolldown/binding-linux-arm64-gnu': 1.2.7 + '@rolldown/binding-linux-arm64-musl': 1.2.7 + '@rolldown/binding-linux-ppc64-gnu': 1.2.7 + '@rolldown/binding-linux-s390x-gnu': 1.2.7 + '@rolldown/binding-linux-x64-gnu': 1.2.7 + '@rolldown/binding-linux-x64-musl': 1.2.7 + '@rolldown/binding-openharmony-arm64': 1.2.7 + '@rolldown/binding-win32-arm64-msvc': 1.2.7 + '@rolldown/binding-win32-x64-msvc': 1.2.7 + + satteri@0.10.5: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + optionalDependencies: + '@bruits/satteri-darwin-arm64': 0.10.5 + '@bruits/satteri-darwin-x64': 0.10.5 + '@bruits/satteri-linux-arm64-gnu': 0.10.5 + '@bruits/satteri-linux-arm64-musl': 0.10.5 + '@bruits/satteri-linux-x64-gnu': 0.10.5 + '@bruits/satteri-linux-x64-musl': 0.10.5 + '@bruits/satteri-wasm32-wasi': 0.10.5 + '@bruits/satteri-win32-arm64-msvc': 0.10.5 + '@bruits/satteri-win32-x64-msvc': 0.10.5 + + sax@1.6.1: {} + + semver@7.8.5: {} + + sharp@0.35.4(@types/node@24.13.3): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 + '@types/node': 24.13.3 + optional: true + + shiki@4.4.3: + dependencies: + '@shikijs/core': 4.4.3 + '@shikijs/engine-javascript': 4.4.3 + '@shikijs/engine-oniguruma': 4.4.3 + '@shikijs/langs': 4.4.3 + '@shikijs/themes': 4.4.3 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + sisteransi@1.0.5: {} + + sitemap@9.0.1: + dependencies: + '@types/node': 24.13.3 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.6.1 + + smol-toml@1.8.0: {} + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + svgo@4.1.0: + dependencies: + commander: 11.1.0 + css-select: 6.0.0 + css-tree: 3.2.1 + css-what: 7.0.0 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.1 + + three@0.185.1: {} + + tiny-inflate@1.0.3: {} + + tinyclip@0.1.15: {} + + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + tslib@2.8.1: + optional: true + + ufo@1.6.4: {} + + ultrahtml@1.7.0: {} + + uncrypto@0.1.3: {} + + undici-types@7.18.2: {} + + undici@8.10.1: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unifont@0.7.5: + dependencies: + css-tree: 3.2.1 + ohash: 2.0.12 + undici: 8.10.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unstorage@1.17.5: + dependencies: + anymatch: 3.1.3 + chokidar: 5.0.0 + destr: 2.0.5 + h3: 1.15.11 + lru-cache: 11.5.2 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.4 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.27 + rolldown: 1.2.7 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.3 + esbuild: 0.28.2 + fsevents: 2.3.3 + + vitefu@1.1.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)): + optionalDependencies: + vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2) + + xxhash-wasm@1.1.0: {} + + yargs-parser@22.0.0: {} + + yocto-queue@1.2.2: {} + + zod@4.5.4: {} + + zwitch@2.0.4: {} diff --git a/website/pnpm-workspace.yaml b/website/pnpm-workspace.yaml new file mode 100644 index 0000000..5ed0b5a --- /dev/null +++ b/website/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true diff --git a/website/public/images/floatick-product-hero-v6.jpg b/website/public/images/floatick-product-hero-v6.jpg index 9a1b452..071dabe 100644 Binary files a/website/public/images/floatick-product-hero-v6.jpg and b/website/public/images/floatick-product-hero-v6.jpg differ diff --git a/website/public/images/floatick-screenshot-editor.png b/website/public/images/floatick-screenshot-editor.png new file mode 100644 index 0000000..0f44bc3 Binary files /dev/null and b/website/public/images/floatick-screenshot-editor.png differ diff --git a/website/public/images/floatick-screenshot-notes.png b/website/public/images/floatick-screenshot-notes.png new file mode 100644 index 0000000..469336f Binary files /dev/null and b/website/public/images/floatick-screenshot-notes.png differ diff --git a/website/public/images/floatick-screenshot-settings.png b/website/public/images/floatick-screenshot-settings.png new file mode 100644 index 0000000..fafc84a Binary files /dev/null and b/website/public/images/floatick-screenshot-settings.png differ diff --git a/website/public/images/floatick-screenshot-todos.png b/website/public/images/floatick-screenshot-todos.png new file mode 100644 index 0000000..aa8cc2d Binary files /dev/null and b/website/public/images/floatick-screenshot-todos.png differ diff --git a/website/public/images/menubar/battery.png b/website/public/images/menubar/battery.png new file mode 100644 index 0000000..9f6cac3 Binary files /dev/null and b/website/public/images/menubar/battery.png differ diff --git a/website/public/images/menubar/control-center.png b/website/public/images/menubar/control-center.png new file mode 100644 index 0000000..a2c2d68 Binary files /dev/null and b/website/public/images/menubar/control-center.png differ diff --git a/website/public/images/menubar/floatick.png b/website/public/images/menubar/floatick.png new file mode 100644 index 0000000..0c843e7 Binary files /dev/null and b/website/public/images/menubar/floatick.png differ diff --git a/website/public/images/menubar/spotlight.png b/website/public/images/menubar/spotlight.png new file mode 100644 index 0000000..224a306 Binary files /dev/null and b/website/public/images/menubar/spotlight.png differ diff --git a/website/public/images/menubar/wifi.png b/website/public/images/menubar/wifi.png new file mode 100644 index 0000000..8ec3dfa Binary files /dev/null and b/website/public/images/menubar/wifi.png differ diff --git a/website/public/images/product-editor-en.png b/website/public/images/product-editor-en.png new file mode 100644 index 0000000..ad66c13 Binary files /dev/null and b/website/public/images/product-editor-en.png differ diff --git a/website/public/images/product-editor-zh.png b/website/public/images/product-editor-zh.png new file mode 100644 index 0000000..d487bce Binary files /dev/null and b/website/public/images/product-editor-zh.png differ diff --git a/website/public/images/product-notes-en.png b/website/public/images/product-notes-en.png new file mode 100644 index 0000000..3d3b52f Binary files /dev/null and b/website/public/images/product-notes-en.png differ diff --git a/website/public/images/product-notes-zh.png b/website/public/images/product-notes-zh.png new file mode 100644 index 0000000..ce51311 Binary files /dev/null and b/website/public/images/product-notes-zh.png differ diff --git a/website/public/images/product-todos-en.png b/website/public/images/product-todos-en.png new file mode 100644 index 0000000..13a2318 Binary files /dev/null and b/website/public/images/product-todos-en.png differ diff --git a/website/public/images/product-todos-zh.png b/website/public/images/product-todos-zh.png new file mode 100644 index 0000000..e3db97c Binary files /dev/null and b/website/public/images/product-todos-zh.png differ diff --git a/website/scripts/export-menubar-symbols.swift b/website/scripts/export-menubar-symbols.swift new file mode 100644 index 0000000..926875f --- /dev/null +++ b/website/scripts/export-menubar-symbols.swift @@ -0,0 +1,28 @@ +import AppKit + +guard CommandLine.arguments.count == 2 else { + print("Usage: xcrun swift export-menubar-symbols.swift ") + exit(1) +} +let output = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true) +try FileManager.default.createDirectory(at: output, withIntermediateDirectories: true) +let symbols = [("wifi", "wifi"), ("battery.100percent", "battery"), ("magnifyingglass", "spotlight"), ("switch.2", "control-center")] +for (symbol, filename) in symbols { + guard let source = NSImage(systemSymbolName: symbol, accessibilityDescription: nil), + let configured = source.withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 13, weight: .medium)) else { + fatalError("Missing symbol: \(symbol)") + } + let canvas = NSSize(width: 26, height: 20) + let bitmap = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: 78, pixelsHigh: 60, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0)! + bitmap.size = canvas + NSGraphicsContext.saveGraphicsState() + NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: bitmap) + NSColor.clear.setFill() + NSRect(origin: .zero, size: canvas).fill() + let natural = configured.size + let scale = min(23 / natural.width, 16 / natural.height) + let size = NSSize(width: natural.width * scale, height: natural.height * scale) + configured.draw(in: NSRect(x: (canvas.width-size.width)/2, y: (canvas.height-size.height)/2, width:size.width, height:size.height), from:.zero, operation:.sourceOver, fraction:1) + NSGraphicsContext.restoreGraphicsState() + try bitmap.representation(using: .png, properties: [:])!.write(to: output.appendingPathComponent(filename + ".png")) +} diff --git a/website/src/components/ChangelogPage.astro b/website/src/components/ChangelogPage.astro index 2990f22..5b46293 100644 --- a/website/src/components/ChangelogPage.astro +++ b/website/src/components/ChangelogPage.astro @@ -6,6 +6,7 @@ import { import { latestDownloadUrl, repositoryUrl, + stableRelease, } from '../config/site-links'; import type { Locale } from '../content/site-copy'; import SiteLayout from '../layouts/SiteLayout.astro'; @@ -90,7 +91,7 @@ const languageHref = isEnglish ? '/zh/changelog/' : '/changelog/'; aria-label={isEnglish ? 'Floatick releases' : 'Floatick 版本'} > { - entries.map((entry, index) => ( + entries.map((entry) => (