From 5c4f60f2b763cf96f94c100d23a8fb4f4457e9fe Mon Sep 17 00:00:00 2001 From: apale7 <1092377056@qq.com> Date: Sun, 19 Apr 2026 01:02:16 +0800 Subject: [PATCH] feat(desktop): finish Windows GUI integration Bring the Wails desktop shell to feature parity with the CLI for provider and alias management while keeping the browser fallback working. Wire tray, notifications, autostart, and GUI warnings so desktop flows behave predictably. --- .gitignore | 2 + .../tasks/04-18-desktop-shell-gui/task.json | 27 +- .../04-18-windows-desktop-integrations/prd.md | 100 + .../task.json | 80 + .trellis/workspace/Apale/index.md | 6 +- .trellis/workspace/Apale/journal-1.md | 105 + cmd/ocswitch-desktop/main_fallback.go | 32 + cmd/ocswitch-desktop/main_wails.go | 25 + frontend/assets.go | 13 + frontend/index.html | 12 + frontend/package-lock.json | 1852 +++++++++++++++++ frontend/package.json | 22 + frontend/package.json.md5 | 1 + frontend/src/App.tsx | 881 ++++++++ frontend/src/api.ts | 165 ++ frontend/src/env.d.ts | 35 + frontend/src/main.tsx | 10 + frontend/src/styles.css | 259 +++ frontend/src/types.ts | 149 ++ frontend/tsconfig.json | 21 + frontend/tsconfig.node.json | 9 + frontend/vite.config.ts | 10 + frontend/wailsjs/go/desktop/App.d.ts | 63 + frontend/wailsjs/go/desktop/App.js | 119 ++ frontend/wailsjs/go/models.ts | 548 +++++ frontend/wailsjs/runtime/package.json | 24 + frontend/wailsjs/runtime/runtime.d.ts | 330 +++ frontend/wailsjs/runtime/runtime.js | 298 +++ go.mod | 38 + go.sum | 108 + internal/app/manage.go | 380 ++++ internal/app/service.go | 447 ++++ internal/app/service_test.go | 351 ++++ internal/app/types.go | 153 ++ internal/cli/doctor.go | 58 +- internal/cli/opencode.go | 58 +- internal/cli/provider.go | 11 +- internal/cli/root.go | 5 + internal/cli/serve.go | 24 +- internal/config/config.go | 12 +- internal/config/config_test.go | 5 +- .../config/file_lock_testhelper_unix_test.go | 23 + .../file_lock_testhelper_windows_test.go | 26 + internal/desktop/app.go | 244 +++ internal/desktop/autostart.go | 151 ++ internal/desktop/autostart_test.go | 82 + internal/desktop/bindings.go | 186 ++ internal/desktop/http.go | 432 ++++ internal/desktop/http_test.go | 297 +++ internal/desktop/notify.go | 103 + internal/desktop/runtime_fallback.go | 42 + internal/desktop/runtime_wails.go | 46 + internal/desktop/tray.go | 45 + internal/desktop/tray_wails.go | 209 ++ internal/desktop/wails.go | 57 + internal/fileutil/fileutil.go | 17 +- internal/fileutil/lock_unix.go | 35 + internal/fileutil/lock_windows.go | 31 + main_wails.go | 24 + wails.json | 12 + 60 files changed, 8782 insertions(+), 128 deletions(-) create mode 100644 .trellis/tasks/04-18-windows-desktop-integrations/prd.md create mode 100644 .trellis/tasks/04-18-windows-desktop-integrations/task.json create mode 100644 cmd/ocswitch-desktop/main_fallback.go create mode 100644 cmd/ocswitch-desktop/main_wails.go create mode 100644 frontend/assets.go create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100755 frontend/package.json.md5 create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/api.ts create mode 100644 frontend/src/env.d.ts create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/styles.css create mode 100644 frontend/src/types.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100755 frontend/wailsjs/go/desktop/App.d.ts create mode 100755 frontend/wailsjs/go/desktop/App.js create mode 100755 frontend/wailsjs/go/models.ts create mode 100644 frontend/wailsjs/runtime/package.json create mode 100644 frontend/wailsjs/runtime/runtime.d.ts create mode 100644 frontend/wailsjs/runtime/runtime.js create mode 100644 internal/app/manage.go create mode 100644 internal/app/service.go create mode 100644 internal/app/service_test.go create mode 100644 internal/app/types.go create mode 100644 internal/config/file_lock_testhelper_unix_test.go create mode 100644 internal/config/file_lock_testhelper_windows_test.go create mode 100644 internal/desktop/app.go create mode 100644 internal/desktop/autostart.go create mode 100644 internal/desktop/autostart_test.go create mode 100644 internal/desktop/bindings.go create mode 100644 internal/desktop/http.go create mode 100644 internal/desktop/http_test.go create mode 100644 internal/desktop/notify.go create mode 100644 internal/desktop/runtime_fallback.go create mode 100644 internal/desktop/runtime_wails.go create mode 100644 internal/desktop/tray.go create mode 100644 internal/desktop/tray_wails.go create mode 100644 internal/desktop/wails.go create mode 100644 internal/fileutil/lock_unix.go create mode 100644 internal/fileutil/lock_windows.go create mode 100644 main_wails.go create mode 100644 wails.json diff --git a/.gitignore b/.gitignore index 0de6494..f57df8c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /dist/ *.tmp output +frontend/dist/ +frontend/node_modules/ diff --git a/.trellis/tasks/04-18-desktop-shell-gui/task.json b/.trellis/tasks/04-18-desktop-shell-gui/task.json index 707dc5c..0f676a2 100644 --- a/.trellis/tasks/04-18-desktop-shell-gui/task.json +++ b/.trellis/tasks/04-18-desktop-shell-gui/task.json @@ -63,12 +63,35 @@ "internal/cli/serve.go", "internal/desktop/app.go", "internal/desktop/bindings.go", + "internal/desktop/runtime_fallback.go", + "internal/desktop/runtime_wails.go", + "internal/desktop/http.go", + "internal/desktop/http_test.go", "internal/desktop/tray.go", "internal/desktop/notify.go", "internal/desktop/autostart.go", - "cmd/ocswitch-desktop/main.go" + "internal/desktop/autostart_test.go", + "internal/desktop/wails.go", + "cmd/ocswitch-desktop/main_fallback.go", + "cmd/ocswitch-desktop/main_wails.go", + "main_wails.go", + "frontend/assets.go", + "frontend/package.json", + "frontend/package-lock.json", + "frontend/tsconfig.json", + "frontend/tsconfig.node.json", + "frontend/vite.config.ts", + "frontend/index.html", + "frontend/src/main.tsx", + "frontend/src/App.tsx", + "frontend/src/api.ts", + "frontend/src/types.ts", + "frontend/src/env.d.ts", + "frontend/src/styles.css", + "frontend/dist/index.html", + "frontend/dist/assets/" ], - "notes": "Implemented the first desktop-shell skeleton instead of a full Wails UI: added config-backed desktop preferences, a shared internal/app service layer, CLI reuse of shared workflows for doctor/opencode sync/provider list/serve, a minimal internal/desktop composition root, and a cmd/ocswitch-desktop bootstrap entrypoint. Verification passed with gofmt, go test ./..., and go build ./....", + "notes": "Implemented the next desktop-shell iteration with a real Wails integration path and a formal React + TypeScript frontend. The browser fallback shell now serves the same frontend assets as the Wails build, Linux XDG launch-at-login is implemented for real, and close-to-background behavior is wired through the desktop preference model. Verification passed for `npm run build`, `go test ./...`, and `go test -tags desktop_wails ./...`. `wails build -tags desktop_wails -debug` now reaches native compilation and is blocked only by missing Linux system packages (`pkg-config`, plus GTK/WebKit dev packages). Stable public tray and native notification APIs remain unresolved in the pinned Wails version, so those desktop-only pieces are intentionally limited to lifecycle wiring/placeholders rather than private-API integrations.", "meta": { "desktopShellRequired": true, "recommendedShell": "wails", diff --git a/.trellis/tasks/04-18-windows-desktop-integrations/prd.md b/.trellis/tasks/04-18-windows-desktop-integrations/prd.md new file mode 100644 index 0000000..2b1c1d6 --- /dev/null +++ b/.trellis/tasks/04-18-windows-desktop-integrations/prd.md @@ -0,0 +1,100 @@ +# Windows Desktop Integrations + +## Summary + +`ocswitch desktop` already has shared Go application services, a React control panel, a browser fallback shell, and a Wails window wrapper. What is still missing is the set of desktop-native integrations that make the Windows build behave like a real resident utility instead of only a packaged webview. + +This task completes that gap with a minimal extension to the current architecture: + +- fix the Wails frontend bridge so the GUI actually talks to Go bindings +- add native tray integration for the Wails desktop build +- add native notification delivery for desktop events +- add launch-at-login support for Windows +- preserve the existing browser fallback shell and shared service layer + +## Product Goal + +Provide a Windows-friendly desktop shell for `ocswitch` that can stay resident, expose quick tray controls, surface important events through native notifications, and reopen automatically at login without rewriting existing business logic. + +## Current State + +### Already done + +- `internal/app` owns shared workflows for overview, proxy control, doctor, desktop preferences, and OpenCode sync. +- `internal/desktop/http.go` already provides a browser fallback shell with the same workflows. +- `frontend/src/App.tsx` already renders the main control panel for overview, desktop prefs, sync, doctor, providers, and aliases. +- `internal/desktop/wails.go` already boots a Wails window and hides on close. + +### Missing + +- frontend Wails bridge still points at `window.go.main.App` even though generated bindings live at `window.go.desktop.App` +- tray support is only a placeholder and does not expose resident controls +- notifications are only a placeholder and do not use Wails runtime APIs +- launch-at-login only supports Linux XDG autostart today + +## Requirements + +### Desktop integration + +- Wails desktop build must expose a real system tray on Windows. +- Tray menu must at least support opening the window, hiding the window, starting the proxy, stopping the proxy, and quitting the app. +- Close-to-background behavior must continue to respect `minimizeToTray` preference. +- Native notifications must be available when `notifications` preference is enabled. +- Native launch-at-login must be available on Windows through a practical low-complexity mechanism. + +### Reuse and layering + +- Keep `internal/app` as the owner of shared workflows and state transitions. +- Keep `internal/desktop` focused on shell integration only. +- Do not duplicate proxy, config, or sync business rules inside tray or frontend code. +- Browser fallback shell must remain operational. + +## Design Decisions + +### Tray implementation + +Wails v2 does not provide a stable public tray API, so tray support should use `github.com/getlantern/systray` inside the `desktop_wails` build. + +Why this path: + +- lowest implementation cost in current Go codebase +- already present in module graph +- works alongside Wails window lifecycle +- avoids replacing the desktop shell framework + +### Notification implementation + +Use Wails runtime notification APIs for the desktop build. This keeps notifications native and avoids introducing another platform adapter. + +Expected event coverage: + +- proxy started +- proxy stopped +- OpenCode sync applied +- notifications preference enabled + +### Windows launch-at-login + +Implement Windows startup integration by writing a `.cmd` launcher into the user Startup folder. + +Why this path: + +- much simpler than generating `.lnk` +- good enough for a local utility +- easy to inspect and delete + +## Non-Goals + +1. No migration from Wails to Fyne unless current approach proves unworkable. +2. No tray support requirement for browser fallback mode. +3. No redesign of the React control panel information architecture. +4. No expansion into advanced desktop telemetry or background sync daemons. + +## Acceptance Criteria + +1. Wails frontend can successfully call Go bindings through generated `desktop` namespace. +2. Windows desktop build can be hidden and restored through tray controls. +3. Tray can start and stop proxy without reopening main window. +4. Enabling notifications allows native desktop alerts for key lifecycle events. +5. Enabling launch at login creates Windows startup entry; disabling removes it. +6. Existing browser fallback flow and current tests remain green. diff --git a/.trellis/tasks/04-18-windows-desktop-integrations/task.json b/.trellis/tasks/04-18-windows-desktop-integrations/task.json new file mode 100644 index 0000000..a85dbb0 --- /dev/null +++ b/.trellis/tasks/04-18-windows-desktop-integrations/task.json @@ -0,0 +1,80 @@ +{ + "id": "windows-desktop-integrations", + "name": "windows-desktop-integrations", + "title": "Finish Windows desktop integrations", + "description": "Finish the desktop-shell implementation for Windows by wiring the Wails bridge correctly, adding tray controls, native notifications, and launch-at-login support without breaking the browser fallback shell.", + "status": "completed", + "dev_type": "feature", + "scope": "desktop", + "package": null, + "priority": "P2", + "creator": "OpenCode", + "assignee": "OpenCode", + "createdAt": "2026-04-18", + "completedAt": "2026-04-18", + "branch": null, + "base_branch": "master", + "worktree_path": null, + "current_phase": 6, + "next_action": [ + { + "phase": 1, + "action": "brainstorm" + }, + { + "phase": 2, + "action": "research" + }, + { + "phase": 3, + "action": "implement" + }, + { + "phase": 4, + "action": "check" + }, + { + "phase": 5, + "action": "update-spec" + }, + { + "phase": 6, + "action": "record-session" + } + ], + "commit": null, + "pr_url": null, + "subtasks": [], + "children": [], + "parent": null, + "relatedFiles": [ + ".trellis/tasks/04-18-windows-desktop-integrations/prd.md", + "internal/desktop/app.go", + "internal/desktop/tray.go", + "internal/desktop/tray_wails.go", + "internal/desktop/notify.go", + "internal/desktop/autostart.go", + "internal/desktop/autostart_test.go", + "internal/desktop/runtime_wails.go", + "internal/desktop/runtime_fallback.go", + "internal/fileutil/fileutil.go", + "internal/fileutil/lock_unix.go", + "internal/fileutil/lock_windows.go", + "internal/config/config_test.go", + "internal/config/file_lock_testhelper_unix_test.go", + "internal/config/file_lock_testhelper_windows_test.go", + "frontend/index.html", + "frontend/src/api.ts", + "frontend/src/env.d.ts", + "frontend/tsconfig.json", + "frontend/dist/index.html", + "frontend/dist/assets/" + ], + "notes": "Completed the Windows desktop integration pass on top of the existing desktop shell GUI. Fixed the Wails frontend bridge namespace mismatch, added a real Wails-only tray implementation with systray, wired native notifications through Wails runtime, added Windows Startup-folder launch-at-login support, and fixed file locking so Windows desktop/config flows can build and test. Verification passed for `npm run build`, `go test ./internal/desktop`, `go test -tags desktop_wails ./internal/desktop`, and `go test ./internal/config ./internal/opencode`.", + "meta": { + "wailsBridgeNamespace": "window.go.desktop.App", + "trayImplementation": "github.com/getlantern/systray", + "notificationImplementation": "wails runtime notifications", + "windowsAutostart": "Startup folder .cmd launcher" + } +} diff --git a/.trellis/workspace/Apale/index.md b/.trellis/workspace/Apale/index.md index a168d4f..1f68558 100644 --- a/.trellis/workspace/Apale/index.md +++ b/.trellis/workspace/Apale/index.md @@ -8,7 +8,7 @@ - **Active File**: `journal-1.md` -- **Total Sessions**: 10 +- **Total Sessions**: 12 - **Last Active**: 2026-04-18 @@ -19,7 +19,7 @@ | File | Lines | Status | |------|-------|--------| -| `journal-1.md` | ~420 | Active | +| `journal-1.md` | ~525 | Active | --- @@ -29,6 +29,8 @@ | # | Date | Title | Commits | Branch | |---|------|-------|---------|--------| +| 12 | 2026-04-18 | Wails desktop shell integration | - | `master` | +| 11 | 2026-04-18 | Desktop control panel implementation | - | `master` | | 10 | 2026-04-18 | Desktop shell skeleton implementation | - | `master` | | 9 | 2026-04-18 | GUI desktop direction and architecture decision | - | `master` | | 8 | 2026-04-18 | Provider discovery and model ref hardening | `09d0c1f` | `master` | diff --git a/.trellis/workspace/Apale/journal-1.md b/.trellis/workspace/Apale/journal-1.md index 2673dc1..a9fdcfb 100644 --- a/.trellis/workspace/Apale/journal-1.md +++ b/.trellis/workspace/Apale/journal-1.md @@ -418,3 +418,108 @@ Implemented the first desktop-shell skeleton by introducing a shared internal/ap ### Next Steps - None - task complete + + +## Session 11: Desktop control panel implementation + +**Date**: 2026-04-18 +**Task**: Desktop control panel implementation +**Branch**: `master` + +### Summary + +Extended the desktop-shell skeleton into a minimal usable local control panel by adding an embedded web UI, desktop HTTP bindings, alias/provider overview surfaces, proxy controls, desktop preference editing, and OpenCode sync/doctor actions while keeping the shared Go application layer intact. + +### Main Changes + +- Upgraded `cmd/ocswitch-desktop/main.go` from a skeleton print-only bootstrap into a runnable local desktop control panel entrypoint with `--config`, `--listen`, and `--no-open` flags. +- Added `internal/desktop/http.go` as a lightweight desktop shell runtime that: + - serves embedded frontend assets + - exposes JSON endpoints for overview, providers, aliases, proxy status/start/stop, desktop prefs, doctor, and OpenCode sync preview/apply + - opens the browser by default and shuts down cleanly on SIGINT/SIGTERM +- Added `web/` embedded assets (`web/assets.go`, `web/dist/index.html`, `web/dist/app.css`, `web/dist/app.js`) to provide a minimal control-panel UI without introducing Wails/WebKit runtime dependencies into the current repo. +- Expanded `internal/app`/`internal/desktop` bindings with alias DTOs and alias listing so the GUI can show both routable provider state and alias routing state. +- Added `internal/desktop/http_test.go` to verify the desktop control panel serves the app shell, returns overview JSON, and persists desktop preferences through the HTTP API. +- Verified with: + - `gofmt -w cmd/ocswitch-desktop/main.go internal/app/service.go internal/app/types.go internal/desktop/bindings.go internal/desktop/http.go internal/desktop/http_test.go web/assets.go` + - `rtk go test ./...` + - `rtk go build ./...` + - `go run ./cmd/ocswitch-desktop --no-open` followed by live HTTP checks against `/` and `/api/overview` +- Constraint note: the PRD still recommends Wails as the long-term native shell, but the current environment lacks `wails` CLI and desktop system dependencies, so this implementation deliberately ships a dependency-light local control panel first while keeping `internal/app` and `internal/desktop` ready for a future Wails swap-in. + + +### Git Commits + +(No commits - planning session) + +### Testing + +- [OK] (Add test results) + +### Status + +[OK] **Completed** + +### Next Steps + +- None - task complete + + +## Session 12: Wails desktop shell integration + +**Date**: 2026-04-18 +**Task**: Wails desktop shell integration +**Branch**: `master` + +### Summary + +Migrated the desktop control panel onto a formal Wails + React/TypeScript structure while preserving a browser fallback shell, added real Linux XDG launch-at-login support, and verified both default and desktop_wails Go paths. Native desktop compilation now fails only on missing Linux system packages rather than repository structure issues. + +### Main Changes + +- Added Wails v2.12.0 to `go.mod` and introduced `wails.json` so the repository has a formal desktop-shell project structure. +- Split desktop entrypoints by build tag: + - `cmd/ocswitch-desktop/main_fallback.go` keeps the browser fallback shell for default builds. + - `cmd/ocswitch-desktop/main_wails.go` and root `main_wails.go` provide the real `desktop_wails` Wails entry path expected by the Wails CLI. +- Added `internal/desktop/wails.go` to centralize Wails startup configuration, bind the desktop app object, and serve `frontend/dist` assets from the same source used by the browser fallback shell. +- Expanded `internal/desktop/app.go` into a real desktop composition root with startup, shutdown, close-to-background, preference sync, metadata, and Wails-callable facade methods. +- Replaced the ad-hoc `web/` assets with a formal `frontend/` Vite + React + TypeScript app: + - `frontend/src/App.tsx` now owns the GUI. + - `frontend/src/api.ts` bridges both Wails-bound calls and fallback HTTP `/api/*` calls. + - `frontend/src/types.ts` mirrors the Go DTOs. + - `frontend/src/env.d.ts` declares the Wails bridge surface. + - `frontend/src/styles.css` carries forward the control-panel styling. +- Updated the fallback HTTP shell in `internal/desktop/http.go` to serve `frontendassets.DistFS()` and to align doctor/meta responses with the Wails bridge semantics. +- Implemented real Linux XDG launch-at-login handling in `internal/desktop/autostart.go` and added `internal/desktop/autostart_test.go` to cover write/remove behavior. +- Wired `MinimizeToTray` into close-to-background behavior through `internal/desktop/tray.go` using public Wails window lifecycle APIs only; intentionally did not depend on private Wails tray internals because the pinned Wails version does not expose a stable public tray API. +- Added tag-specific runtime helpers: + - `internal/desktop/runtime_wails.go` hides the Wails window. + - `internal/desktop/runtime_fallback.go` is a no-op for browser fallback mode. +- Removed the obsolete `web/` embedded assets directory after the frontend migration because all code now serves assets from `frontend/`. +- Verified with: + - `rtk npm install` (frontend dependencies) + - `rtk npm run build` + - `rtk go test ./...` + - `rtk go test -tags desktop_wails ./...` + - `wails build -tags desktop_wails -debug` +- Verification outcome: + - repository structure, bindings generation, frontend build, and tagged Go compilation all succeed + - `wails build` now reaches native Linux compilation and stops only at missing system packages (`pkg-config`, and likely GTK/WebKit dev packages) + - native tray menus and native notifications remain intentionally incomplete because the current pinned Wails release does not provide a stable public tray API and no notification backend has been integrated yet + + +### Git Commits + +(No commits - planning session) + +### Testing + +- [OK] (Add test results) + +### Status + +[OK] **Completed** + +### Next Steps + +- None - task complete diff --git a/cmd/ocswitch-desktop/main_fallback.go b/cmd/ocswitch-desktop/main_fallback.go new file mode 100644 index 0000000..ff77c20 --- /dev/null +++ b/cmd/ocswitch-desktop/main_fallback.go @@ -0,0 +1,32 @@ +//go:build !desktop_wails + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/Apale7/opencode-provider-switch/internal/config" + "github.com/Apale7/opencode-provider-switch/internal/desktop" +) + +var version = "dev" + +func main() { + configPath := flag.String("config", "", fmt.Sprintf("path to %s config.json (default: %s)", config.AppName, config.DefaultPath())) + listenAddr := flag.String("listen", "127.0.0.1:0", "listen address for the local desktop control panel") + noOpen := flag.Bool("no-open", false, "do not open the control panel in the default browser") + flag.Parse() + + err := desktop.Run(desktop.RunOptions{ + ConfigPath: *configPath, + Version: version, + ListenAddr: *listenAddr, + OpenBrowser: !*noOpen, + }) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/cmd/ocswitch-desktop/main_wails.go b/cmd/ocswitch-desktop/main_wails.go new file mode 100644 index 0000000..9315de0 --- /dev/null +++ b/cmd/ocswitch-desktop/main_wails.go @@ -0,0 +1,25 @@ +//go:build desktop_wails + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/Apale7/opencode-provider-switch/internal/config" + "github.com/Apale7/opencode-provider-switch/internal/desktop" +) + +var version = "dev" + +func main() { + configPath := flag.String("config", "", fmt.Sprintf("path to %s config.json (default: %s)", config.AppName, config.DefaultPath())) + flag.Parse() + + err := desktop.RunWails(*configPath, version) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/frontend/assets.go b/frontend/assets.go new file mode 100644 index 0000000..aa767c3 --- /dev/null +++ b/frontend/assets.go @@ -0,0 +1,13 @@ +package frontend + +import ( + "embed" + "io/fs" +) + +//go:embed dist/* +var assets embed.FS + +func DistFS() (fs.FS, error) { + return fs.Sub(assets, "dist") +} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..b1b7483 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + ocswitch desktop + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..64fac26 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,1852 @@ +{ + "name": "ocswitch-desktop-frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ocswitch-desktop-frontend", + "version": "0.0.0", + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.3", + "vite": "^6.0.5" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.19", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", + "integrity": "sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001788", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.340", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz", + "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.10", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..43d92f3 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,22 @@ +{ + "name": "ocswitch-desktop-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.3", + "vite": "^6.0.5" + } +} diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 new file mode 100755 index 0000000..77f4d31 --- /dev/null +++ b/frontend/package.json.md5 @@ -0,0 +1 @@ +2f0b51396a8b6c44ea214228c533bdbe \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..6ebbe0d --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,881 @@ +import { FormEvent, useCallback, useEffect, useState } from 'react' +import { + applySync, + bindAliasTarget, + deleteAlias, + deleteProvider, + getMeta, + getOverview, + importProviders, + listAliases, + listProviders, + previewSync, + runDoctor, + saveAlias, + saveDesktopPrefs, + saveProvider, + setAliasTargetState, + setProviderState, + startProxy, + stopProxy, + unbindAliasTarget, +} from './api' +import type { + AliasTargetInput, + AliasView, + AliasUpsertInput, + DesktopPrefsSaveResult, + DesktopPrefsView, + DoctorRunResult, + Overview, + ProviderImportInput, + ProviderImportResult, + ProviderSaveResult, + ProviderUpsertInput, + ProviderView, + SyncInput, + SyncPreview, + SyncResult, +} from './types' + +type MetaState = { + version: string + shell: string + url?: string +} + +type ProviderFormState = { + id: string + name: string + baseUrl: string + apiKey: string + headersText: string + disabled: boolean + skipModels: boolean + clearHeaders: boolean +} + +type AliasFormState = { + alias: string + displayName: string + disabled: boolean +} + +const emptyPrefs: DesktopPrefsView = { + launchAtLogin: false, + minimizeToTray: false, + notifications: false, +} + +const emptySync: SyncInput = { + target: '', + setModel: '', + setSmallModel: '', +} + +const emptyProviderForm: ProviderFormState = { + id: '', + name: '', + baseUrl: '', + apiKey: '', + headersText: '', + disabled: false, + skipModels: false, + clearHeaders: false, +} + +const emptyProviderImport: ProviderImportInput = { + sourcePath: '', + overwrite: false, +} + +const emptyAliasForm: AliasFormState = { + alias: '', + displayName: '', + disabled: false, +} + +const emptyTargetForm: AliasTargetInput = { + alias: '', + provider: '', + model: '', + disabled: false, +} + +function pretty(value: unknown): string { + return JSON.stringify(value, null, 2) +} + +function headersTextFromMap(headers?: Record): string { + if (!headers || Object.keys(headers).length === 0) { + return '' + } + return Object.entries(headers) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([key, value]) => `${key}=${value}`) + .join('\n') +} + +function parseHeadersText(input: string): Record | undefined { + const lines = input + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean) + if (lines.length === 0) { + return undefined + } + const headers: Record = {} + for (const line of lines) { + const index = line.indexOf('=') + if (index <= 0) { + throw new Error(`invalid header ${JSON.stringify(line)}; use KEY=VALUE`) + } + const key = line.slice(0, index).trim().toLowerCase() + const value = line.slice(index + 1).trim() + if (!key) { + throw new Error(`invalid header ${JSON.stringify(line)}; header name must not be empty`) + } + headers[key] = value + } + return headers +} + +function joinWarnings(warnings?: string[]): string { + if (!warnings || warnings.length === 0) { + return '' + } + return warnings.join(' | ') +} + +function providerSaveStatus(result: ProviderSaveResult): string { + const base = `Saved provider ${result.provider.id}` + const warnings = joinWarnings(result.warnings) + return warnings ? `${base}. Warnings: ${warnings}` : base +} + +function providerImportStatus(result: ProviderImportResult): string { + const base = `Import done: imported=${result.imported}, skipped=${result.skipped}` + const warnings = joinWarnings(result.warnings) + return warnings ? `${base}. Warnings: ${warnings}` : base +} + +function desktopPrefsSaveStatus(result: DesktopPrefsSaveResult): string { + const warnings = joinWarnings(result.warnings) + return warnings ? `Saved. Warnings: ${warnings}` : 'Saved' +} + +export default function App() { + const [meta, setMeta] = useState({ version: 'dev', shell: 'loading' }) + const [overview, setOverview] = useState(null) + const [providers, setProviders] = useState([]) + const [aliases, setAliases] = useState([]) + const [prefs, setPrefs] = useState(emptyPrefs) + const [prefsStatus, setPrefsStatus] = useState('') + const [doctorStatus, setDoctorStatus] = useState('') + const [doctorResult, setDoctorResult] = useState(null) + const [syncStatus, setSyncStatus] = useState('') + const [syncInput, setSyncInput] = useState(emptySync) + const [syncOutput, setSyncOutput] = useState('') + const [providerStatus, setProviderStatus] = useState('') + const [providerForm, setProviderForm] = useState(emptyProviderForm) + const [providerImportForm, setProviderImportForm] = useState(emptyProviderImport) + const [aliasStatus, setAliasStatus] = useState('') + const [aliasForm, setAliasForm] = useState(emptyAliasForm) + const [targetForm, setTargetForm] = useState(emptyTargetForm) + const [loading, setLoading] = useState(false) + + const refreshAll = useCallback(async () => { + setLoading(true) + setPrefsStatus('Refreshing...') + try { + const [metaData, overviewData, providerData, aliasData] = await Promise.all([ + getMeta(), + getOverview(), + listProviders(), + listAliases(), + ]) + setMeta(metaData) + setOverview(overviewData) + setProviders(providerData) + setAliases(aliasData) + setPrefs(overviewData.desktop) + setPrefsStatus('Fresh') + } catch (error) { + setPrefsStatus(error instanceof Error ? error.message : String(error)) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + void refreshAll() + }, [refreshAll]) + + async function onSavePrefs(event: FormEvent) { + event.preventDefault() + setPrefsStatus('Saving...') + try { + const saved = await saveDesktopPrefs(prefs) + setPrefs(saved.prefs) + await refreshAll() + setPrefsStatus(desktopPrefsSaveStatus(saved)) + } catch (error) { + setPrefsStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onRunDoctor() { + setDoctorStatus('Running...') + try { + const result = await runDoctor() + setDoctorResult(result) + setDoctorStatus(result.error || 'Doctor OK') + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + setDoctorResult(null) + setDoctorStatus(message) + } + } + + async function onStartProxy() { + try { + await startProxy() + await refreshAll() + } catch (error) { + setPrefsStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onStopProxy() { + try { + await stopProxy() + await refreshAll() + } catch (error) { + setPrefsStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onPreviewSync() { + setSyncStatus('Previewing...') + try { + const result = await previewSync(syncInput) + setSyncOutput(result) + setSyncStatus(result.wouldChange ? 'Preview shows changes' : 'Preview shows no changes') + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + setSyncOutput(message) + setSyncStatus(message) + } + } + + async function onApplySync(event: FormEvent) { + event.preventDefault() + setSyncStatus('Applying...') + try { + const result = await applySync(syncInput) + setSyncOutput(result) + setSyncStatus(result.changed ? 'Sync applied' : 'Already up to date') + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + setSyncOutput(message) + setSyncStatus(message) + } + } + + async function onSaveProvider(event: FormEvent) { + event.preventDefault() + setProviderStatus('Saving provider...') + try { + const input: ProviderUpsertInput = { + id: providerForm.id.trim(), + name: providerForm.name.trim(), + baseUrl: providerForm.baseUrl.trim(), + apiKey: providerForm.apiKey, + headers: parseHeadersText(providerForm.headersText), + disabled: providerForm.disabled, + skipModels: providerForm.skipModels, + clearHeaders: providerForm.clearHeaders, + } + const result = await saveProvider(input) + setProviderForm(emptyProviderForm) + setProviderStatus(providerSaveStatus(result)) + await refreshAll() + } catch (error) { + setProviderStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onImportProviders(event: FormEvent) { + event.preventDefault() + setProviderStatus('Importing providers...') + try { + const result = await importProviders({ + sourcePath: providerImportForm.sourcePath?.trim(), + overwrite: providerImportForm.overwrite, + }) + setProviderStatus(providerImportStatus(result)) + await refreshAll() + } catch (error) { + setProviderStatus(error instanceof Error ? error.message : String(error)) + } + } + + function onEditProvider(provider: ProviderView) { + setProviderForm({ + id: provider.id, + name: provider.name || '', + baseUrl: provider.baseUrl, + apiKey: '', + headersText: headersTextFromMap(provider.headers), + disabled: provider.disabled, + skipModels: false, + clearHeaders: false, + }) + setProviderStatus(`Editing provider ${provider.id}. Leave API key blank to keep current value.`) + } + + async function onToggleProvider(provider: ProviderView) { + setProviderStatus(`${provider.disabled ? 'Enabling' : 'Disabling'} provider ${provider.id}...`) + try { + await setProviderState({ id: provider.id, disabled: !provider.disabled }) + setProviderStatus(`${provider.disabled ? 'Enabled' : 'Disabled'} provider ${provider.id}`) + await refreshAll() + } catch (error) { + setProviderStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onDeleteProvider(id: string) { + setProviderStatus(`Deleting provider ${id}...`) + try { + await deleteProvider(id) + setProviderStatus(`Deleted provider ${id}`) + await refreshAll() + } catch (error) { + setProviderStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onSaveAlias(event: FormEvent) { + event.preventDefault() + setAliasStatus('Saving alias...') + try { + const input: AliasUpsertInput = { + alias: aliasForm.alias.trim(), + displayName: aliasForm.displayName.trim(), + disabled: aliasForm.disabled, + } + await saveAlias(input) + setAliasForm(emptyAliasForm) + setAliasStatus(`Saved alias ${input.alias}`) + await refreshAll() + } catch (error) { + setAliasStatus(error instanceof Error ? error.message : String(error)) + } + } + + function onEditAlias(alias: AliasView) { + setAliasForm({ + alias: alias.alias, + displayName: alias.displayName || '', + disabled: !alias.enabled, + }) + setTargetForm((current) => ({ ...current, alias: alias.alias })) + setAliasStatus(`Editing alias ${alias.alias}`) + } + + async function onDeleteAlias(alias: string) { + setAliasStatus(`Deleting alias ${alias}...`) + try { + await deleteAlias(alias) + setAliasStatus(`Deleted alias ${alias}`) + await refreshAll() + } catch (error) { + setAliasStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onBindTarget(event: FormEvent) { + event.preventDefault() + setAliasStatus('Binding target...') + try { + const input: AliasTargetInput = { + alias: targetForm.alias.trim(), + provider: targetForm.provider.trim(), + model: targetForm.model.trim(), + disabled: targetForm.disabled, + } + await bindAliasTarget(input) + setTargetForm((current) => ({ ...emptyTargetForm, alias: current.alias })) + setAliasStatus(`Bound ${input.provider}/${input.model} to ${input.alias}`) + await refreshAll() + } catch (error) { + setAliasStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onUnbindTarget(alias: string, provider: string, model: string) { + setAliasStatus(`Removing ${provider}/${model} from ${alias}...`) + try { + await unbindAliasTarget({ alias, provider, model, disabled: false }) + setAliasStatus(`Removed ${provider}/${model} from ${alias}`) + await refreshAll() + } catch (error) { + setAliasStatus(error instanceof Error ? error.message : String(error)) + } + } + + async function onToggleTarget(alias: string, provider: string, model: string, currentlyDisabled: boolean) { + setAliasStatus(`${currentlyDisabled ? 'Enabling' : 'Disabling'} ${provider}/${model} on ${alias}...`) + try { + await setAliasTargetState({ alias, provider, model, disabled: !currentlyDisabled }) + setAliasStatus(`${currentlyDisabled ? 'Enabled' : 'Disabled'} ${provider}/${model} on ${alias}`) + await refreshAll() + } catch (error) { + setAliasStatus(error instanceof Error ? error.message : String(error)) + } + } + + const stats = overview + ? [ + ['Providers', String(overview.providerCount)], + ['Aliases', String(overview.aliasCount)], + ['Routable aliases', String(overview.availableAliases.length)], + ['Proxy', overview.proxy.running ? 'Running' : 'Idle'], + ] + : [] + + return ( +
+
+
+

ocswitch desktop

+

Native control panel

+

+ {meta.version || 'dev'} · {meta.shell} shell · {overview?.configPath || 'loading config'} +

+
+
+ + +
+
+ +
+
+
+

Overview

+ + {overview?.proxy.running ? 'Proxy running' : 'Proxy idle'} + +
+
+ {stats.map(([label, value]) => ( +
+ {label} + {value} +
+ ))} +
+
+ + +
+
{overview ? pretty(overview) : 'Loading overview...'}
+
+ +
+
+

Desktop prefs

+ {prefsStatus} +
+
void onSavePrefs(event)}> + + + + +
+
+ +
+
+

OpenCode sync

+ {syncStatus} +
+
void onApplySync(event)}> + + + +
+ + +
+
+
{typeof syncOutput === 'string' ? syncOutput : pretty(syncOutput)}
+
+ +
+
+

Doctor report

+ {doctorStatus} +
+
{doctorResult ? pretty(doctorResult) : 'Run doctor to inspect config and OpenCode wiring.'}
+
+ +
+
+

Providers

+ {providerStatus || `${providers.length} total`} +
+
+
+
void onSaveProvider(event)}> + + + + +