From 232444f3e01e1cac25e5805b8c4a8a5f25cc430b Mon Sep 17 00:00:00 2001 From: Axel Rindle Date: Mon, 3 Aug 2026 08:53:39 +0200 Subject: [PATCH 01/13] Add --json flag to status + list cmd --- internal/cli/cli.go | 51 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/internal/cli/cli.go b/internal/cli/cli.go index bdabf0f..16ea77a 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -8,12 +8,14 @@ package cli import ( "context" + "encoding/json" "fmt" "io" "log/slog" "net" "os" "path/filepath" + "slices" "strconv" "strings" "time" @@ -78,8 +80,8 @@ func usage(w io.Writer) { fmt.Fprint(w, `wireguide ctl — control the WireGuide helper from the command line Tunnels: - wireguide ctl status show connection status - wireguide ctl list list tunnels (● = connected) + wireguide ctl status [--json] show connection status + wireguide ctl list [--json] list tunnels (● = connected) wireguide ctl connect connect a tunnel wireguide ctl disconnect [name] disconnect one tunnel (or all) wireguide ctl import [name] import a .conf (name defaults to filename) @@ -153,7 +155,9 @@ func tunnelStore() (*storage.TunnelStore, error) { return storage.NewTunnelStore(paths.TunnelsDir), nil } -func cmdStatus(_ []string) int { +func cmdStatus(args []string) int { + jsonOut := hasFlag(args, "--json") + c, err := dialHelper() if err != nil { fmt.Fprintln(os.Stderr, err) @@ -167,6 +171,9 @@ func cmdStatus(_ []string) int { return 1 } if len(active.Names) == 0 { + if jsonOut { + return printJSON([]domain.ConnectionStatus{}) + } fmt.Println("disconnected") return 0 } @@ -180,6 +187,9 @@ func cmdStatus(_ []string) int { if len(rows) == 0 { rows = []domain.ConnectionStatus{st} } + if jsonOut { + return printJSON(rows) + } for _, r := range rows { hs := r.LastHandshake if hs == "" { @@ -191,7 +201,16 @@ func cmdStatus(_ []string) int { return 0 } -func cmdList(_ []string) int { +// tunnelListEntry is the --json shape for `ctl list`; domain.ConnectionStatus +// doesn't apply here since a listed tunnel may never have been connected. +type tunnelListEntry struct { + Name string `json:"name"` + Active bool `json:"active"` +} + +func cmdList(args []string) int { + jsonOut := hasFlag(args, "--json") + store, err := tunnelStore() if err != nil { fmt.Fprintln(os.Stderr, "list:", err) @@ -214,6 +233,13 @@ func cmdList(_ []string) int { } c.Close() } + if jsonOut { + entries := make([]tunnelListEntry, len(names)) + for i, n := range names { + entries[i] = tunnelListEntry{Name: n, Active: activeSet[n]} + } + return printJSON(entries) + } if len(names) == 0 { fmt.Println("(no tunnels)") return 0 @@ -228,6 +254,23 @@ func cmdList(_ []string) int { return 0 } +// hasFlag reports whether flag is present anywhere in args. +func hasFlag(args []string, flag string) bool { + return slices.Contains(args, flag) +} + +// printJSON marshals v as indented JSON to stdout. Always returns 0 unless +// marshalling itself fails. +func printJSON(v any) int { + data, err := json.MarshalIndent(v, "", " ") + if err != nil { + fmt.Fprintln(os.Stderr, "json:", err) + return 1 + } + fmt.Println(string(data)) + return 0 +} + func cmdConnect(args []string) int { if len(args) < 1 { fmt.Fprintln(os.Stderr, "usage: wireguide ctl connect ") From 2323b58b82ce3261f3dadf861959b96a6f20bd8e Mon Sep 17 00:00:00 2001 From: korjwl1 <72062804+korjwl1@users.noreply.github.com> Date: Tue, 4 Aug 2026 01:27:22 +0900 Subject: [PATCH 02/13] test and harden Linux support --- .github/workflows/linux-ci.yml | 80 ++ .github/workflows/release.yml | 18 +- .gitignore | 1 + CONTRIBUTING.md | 4 +- README.ko.md | 4 +- README.md | 4 +- build/Taskfile.yml | 2 +- build/linux/Taskfile.yml | 12 + build/linux/nfpm/nfpm.yaml | 21 +- docs/linux-test-plan.md | 84 ++ frontend/package-lock.json | 1568 +++++++++------------- frontend/package.json | 28 +- frontend/public/style.css | 9 +- frontend/src/main.js | 3 +- frontend/vite.config.js | 52 +- go.mod | 22 +- go.sum | 48 +- internal/gui/dock_other.go | 12 +- internal/gui/gui.go | 28 +- internal/gui/tray.go | 9 +- internal/ipc/ipc_test.go | 5 +- internal/reconnect/network_linux.go | 176 ++- internal/reconnect/network_linux_test.go | 43 + 23 files changed, 1104 insertions(+), 1129 deletions(-) create mode 100644 .github/workflows/linux-ci.yml create mode 100644 docs/linux-test-plan.md create mode 100644 internal/reconnect/network_linux_test.go diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml new file mode 100644 index 0000000..77209bd --- /dev/null +++ b/.github/workflows/linux-ci.yml @@ -0,0 +1,80 @@ +name: Linux CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + test-build-package: + name: Ubuntu amd64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '1.25.12' + + - uses: actions/setup-node@v4 + with: + node-version: '20.19.2' + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install Linux build dependencies + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev + + - name: Install pinned build tools + run: | + go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74 + go install github.com/go-task/task/v3/cmd/task@v3.45.4 + echo "$HOME/go/bin" >> "$GITHUB_PATH" + + # main.go embeds frontend/dist, so the frontend must exist before any + # command that loads the root Go package (test, vet, or bindings). + - name: Build frontend + working-directory: frontend + run: | + npm ci + npm run build + + - name: Test and vet + run: | + go test -race ./... + go vet ./... + + - name: Build native Linux binary + run: task linux:build ARCH=amd64 + + - name: Check runtime linking + run: | + file bin/wireguide + if ldd bin/wireguide | grep -q 'not found'; then + ldd bin/wireguide + exit 1 + fi + + - name: Build and inspect DEB + env: + GOARCH: amd64 + GIT_COMMITTER_NAME: WireGuide CI + GIT_COMMITTER_EMAIL: noreply@example.com + run: | + wails3 tool package -name wireguide -format deb -config ./build/linux/nfpm/nfpm.yaml -out ./bin + dpkg-deb --info bin/wireguide.deb + dpkg-deb --contents bin/wireguide.deb + + - uses: actions/upload-artifact@v4 + with: + name: linux-amd64-ci + path: | + bin/wireguide + bin/wireguide.deb + if-no-files-found: error + retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf85b2a..8e1cafd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,16 +34,16 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.25.12' - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '20.19.2' - name: Install wails3 + task run: | - go install github.com/wailsapp/wails/v3/cmd/wails3@latest - go install github.com/go-task/task/v3/cmd/task@latest + go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74 + go install github.com/go-task/task/v3/cmd/task@v3.45.4 echo "$HOME/go/bin" >> "$GITHUB_PATH" - name: Build .app bundle (ad-hoc signed) @@ -87,17 +87,17 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.25.12' - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '20.19.2' - name: Install wails3 + task shell: pwsh run: | - go install github.com/wailsapp/wails/v3/cmd/wails3@latest - go install github.com/go-task/task/v3/cmd/task@latest + go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74 + go install github.com/go-task/task/v3/cmd/task@v3.45.4 # Append, don't overwrite — PATH already has system entries. "$env:USERPROFILE\go\bin" | Out-File -FilePath $env:GITHUB_PATH -Append @@ -253,7 +253,7 @@ jobs: - uses: actions/setup-go@v5 with: - go-version: '1.25' + go-version: '1.25.12' - name: Sign SHA256SUMS (Ed25519) # HARD failure when the key is missing or mismatched — every diff --git a/.gitignore b/.gitignore index 5ed7ea3..88294fc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ bin frontend/dist frontend/node_modules build/linux/appimage/build +build/linux/wireguide.desktop build/windows/nsis/MicrosoftEdgeWebview2Setup.exe # Go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f02398..b19e662 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,14 +8,14 @@ Thanks for your interest in contributing! - Go 1.25+ - Node.js 20+ -- [Wails v3](https://v3alpha.wails.io/) (`go install github.com/wailsapp/wails/v3/cmd/wails3@latest`) +- [Wails v3](https://v3alpha.wails.io/) (`go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74`) - macOS with Apple Silicon (for now) ### Build & Run ```bash # Install frontend dependencies -cd frontend && npm install && cd .. +cd frontend && npm ci && cd .. # Development mode (hot reload) task dev diff --git a/README.ko.md b/README.ko.md index be0abd5..f315d65 100644 --- a/README.ko.md +++ b/README.ko.md @@ -65,8 +65,8 @@ brew install --cask wireguide ```bash brew install go node -go install github.com/go-task/task/v3/cmd/task@latest -go install github.com/wailsapp/wails/v3/cmd/wails3@latest +go install github.com/go-task/task/v3/cmd/task@v3.45.4 +go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74 task build ./bin/wireguide diff --git a/README.md b/README.md index 879c0ec..ff38ebd 100644 --- a/README.md +++ b/README.md @@ -136,8 +136,8 @@ installer registers the helper service and shortcut. ```bash brew install go node -go install github.com/go-task/task/v3/cmd/task@latest -go install github.com/wailsapp/wails/v3/cmd/wails3@latest +go install github.com/go-task/task/v3/cmd/task@v3.45.4 +go install github.com/wailsapp/wails/v3/cmd/wails3@v3.0.0-alpha.74 task build ./bin/wireguide diff --git a/build/Taskfile.yml b/build/Taskfile.yml index 1819c53..336bdf0 100644 --- a/build/Taskfile.yml +++ b/build/Taskfile.yml @@ -19,7 +19,7 @@ tasks: - sh: npm version msg: "Looks like npm isn't installed. Npm is part of the Node installer: https://nodejs.org/en/download/" cmds: - - npm install + - npm ci build:frontend: label: build:frontend (DEV={{.DEV}}) diff --git a/build/linux/Taskfile.yml b/build/linux/Taskfile.yml index ac92ad1..90ef4af 100644 --- a/build/linux/Taskfile.yml +++ b/build/linux/Taskfile.yml @@ -142,6 +142,8 @@ tasks: cmds: - task: generate:dotdesktop - task: generate:deb + vars: + ARCH: '{{.ARCH | default ARCH}}' create:rpm: summary: Creates a rpm package @@ -150,6 +152,8 @@ tasks: cmds: - task: generate:dotdesktop - task: generate:rpm + vars: + ARCH: '{{.ARCH | default ARCH}}' create:aur: summary: Creates a arch linux packager package @@ -158,19 +162,27 @@ tasks: cmds: - task: generate:dotdesktop - task: generate:aur + vars: + ARCH: '{{.ARCH | default ARCH}}' generate:deb: summary: Creates a deb package + env: + GOARCH: '{{.ARCH | default ARCH}}' cmds: - wails3 tool package -name "{{.APP_NAME}}" -format deb -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin generate:rpm: summary: Creates a rpm package + env: + GOARCH: '{{.ARCH | default ARCH}}' cmds: - wails3 tool package -name "{{.APP_NAME}}" -format rpm -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin generate:aur: summary: Creates a arch linux packager package + env: + GOARCH: '{{.ARCH | default ARCH}}' cmds: - wails3 tool package -name "{{.APP_NAME}}" -format archlinux -config ./build/linux/nfpm/nfpm.yaml -out {{.ROOT_DIR}}/bin diff --git a/build/linux/nfpm/nfpm.yaml b/build/linux/nfpm/nfpm.yaml index 356e2f2..2832d37 100644 --- a/build/linux/nfpm/nfpm.yaml +++ b/build/linux/nfpm/nfpm.yaml @@ -24,20 +24,35 @@ contents: - src: "./build/linux/wireguide.desktop" dst: "/usr/share/applications/wireguide.desktop" -# Default dependencies for Debian 12/Ubuntu 22.04+ with WebKit 4.1 +# Wails v3 alpha.74 uses GTK3 and the WebKitGTK 4.1 API on Linux. depends: - libgtk-3-0 - libwebkit2gtk-4.1-0 + - libayatana-appindicator3-1 + - iproute2 + - nftables + - pkexec | policykit-1 + +# NetworkManager supplies SSID detection and immediate Wi-Fi change events, +# while resolvconf avoids the guarded /etc/resolv.conf fallback on systems +# that do not run systemd-resolved. Noto supplies Korean UI glyphs and stable +# cross-platform text metrics. The core client can still operate without these, +# so keep them as recommendations rather than hard dependencies. +recommends: + - network-manager + - resolvconf + - fonts-noto-core + - fonts-noto-cjk # Distribution-specific overrides for different package formats and WebKit versions overrides: - # RPM packages for RHEL/CentOS/AlmaLinux/Rocky Linux (WebKit 4.0) + # RPM package names used by current Fedora/RHEL-family distributions. rpm: depends: - gtk3 - webkit2gtk4.1 - # Arch Linux packages (WebKit 4.1) + # Arch Linux package names. archlinux: depends: - gtk3 diff --git a/docs/linux-test-plan.md b/docs/linux-test-plan.md new file mode 100644 index 0000000..7bae58a --- /dev/null +++ b/docs/linux-test-plan.md @@ -0,0 +1,84 @@ +# Linux verification plan + +Target baseline: Debian 13 / Raspberry Pi OS, ARM64, Wayland (`labwc`) and +WayVNC. CI baseline: Ubuntu latest, AMD64. + +## Automated gate + +- Clean frontend install and production build (`npm ci`, `npm run build`). +- Go unit tests and vet after `frontend/dist` exists. +- Race tests on Linux AMD64 CI. Raspberry Pi's 39-bit ARM64 VMA layout is not + supported by Go's race runtime, so `-race` cannot run on this device. +- Native Wails production build and `ldd` missing-library check. +- DEB generation, metadata inspection, desktop-file validation, and simulated + APT installation. +- Repeat the native build on ARM64 hardware before a release. + +## GUI and desktop integration + +- Launch under X11/Xvfb for a crash smoke test. +- Launch in a real Wayland session and verify initial window, resizing, + minimising, close-to-tray, tray menu actions, theme, scaling, and dialogs. +- On Raspberry Pi's detached/headless labwc output, also test through XWayland + (`GDK_BACKEND=x11`). Native Wayland WebKitGTK rendering is corrupted on that + virtual output even with the original dependency set, while XWayland renders + correctly. This is a test-host compositor limitation, not a frontend result. +- On the Pi, verified the real Linux tray click path (not only its menu): native + close hides the window without terminating the process, and a left click on + the tray icon restores a decorated window. +- Confirm the PolicyKit prompt appears, cancellation is handled, successful + authentication starts the helper, and closing the last GUI causes the idle + helper to exit. +- Verify XDG autostart creation/removal and launch after a fresh login. +- Install the DEB, check application-menu/icon/tray integration, then purge it + and confirm no package-owned files remain. + +## Linux-only network behaviour + +### Raspberry Pi smoke-test result (2026-08-04) + +- Imported and connected a disposable WireGuard tunnel through the real helper. +- Confirmed the `wg` interface was up with MTU 1420 and the configured IPv4 + route was installed. +- Disconnected and deleted the tunnel, then confirmed both the interface and + route were removed. +- Confirmed routine RTNETLINK traffic no longer produces false primary-network + changes; reconnect decisions now compare the actual default route snapshot. +- This local peer intentionally had no server, so handshake, payload, DNS and + kill-switch verification still require a real test endpoint. + +Run these with a local console or a second management path. A full-tunnel or +kill-switch defect can sever the SSH/VNC session used to test it. + +- Helper Unix socket: directory ownership/mode, peer UID rejection, stale + socket recovery, GUI reconnect, helper version replacement. +- TUN: create/configure/remove the interface and recover after forced helper + termination. +- Routing: IPv4/IPv6 split tunnel, dual-stack full tunnel, endpoint bypass, + custom `Table`/`FwMark`, repeated connect/disconnect, and no leaked routes or + policy rules. +- DNS: test systemd-resolved, resolvconf, and plain `/etc/resolv.conf` paths; + verify restoration after disconnect, helper crash, and reboot. +- Firewall: kill switch and DNS leak protection using nftables; verify LAN and + endpoint exceptions and cleanup after every failure path. +- Network changes: NetworkManager Wi-Fi SSID events, gateway MAC matching, + Ethernet/Wi-Fi handover, DHCP/default-route change, offline/online recovery. +- Power: logind suspend/resume reconnect and fallback polling on systems + without logind. + +## Cross-OS comparison points + +| Capability | Linux | macOS | Windows | +|---|---|---|---| +| Elevation/helper | PolicyKit + Unix socket/peer UID | launchd helper | UAC + named pipe | +| Tunnel/routes | TUN + `ip` policy routing | utun + route socket | Wintun/IP Helper APIs | +| DNS | resolved/resolvconf/resolv.conf | networksetup | Windows IP APIs | +| Kill switch | nftables | pf | Windows firewall/WFP path | +| Wi-Fi identity | NetworkManager + `/proc` ARP | CoreWLAN | WLAN/IP Helper APIs | +| Sleep/network events | logind + netlink | IOKit/SystemConfiguration | Windows notifications | +| Autostart | XDG desktop entry | LaunchAgent | registry Run key | +| Desktop shell | GTK/WebKitGTK + AppIndicator | AppKit/WebKit | WebView2/tray | + +For every platform-sensitive test, compare the observable contract rather +than the implementation: identical tunnel status, route/DNS cleanup, reconnect +timing, error text, persisted settings, and tray/window state. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3f6e3a9..7f8d615 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,43 +8,28 @@ "name": "frontend", "version": "0.0.0", "dependencies": { - "@codemirror/autocomplete": "^6.20.1", - "@codemirror/commands": "^6.10.3", - "@codemirror/language": "^6.12.3", - "@codemirror/lint": "^6.9.5", - "@codemirror/search": "^6.6.0", - "@codemirror/state": "^6.6.0", - "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.41.0", - "@lezer/highlight": "^1.2.3", - "@wailsio/runtime": "latest", - "codemirror": "^6.0.2" + "@codemirror/autocomplete": "6.20.3", + "@codemirror/commands": "6.10.4", + "@codemirror/language": "6.12.4", + "@codemirror/lint": "6.9.7", + "@codemirror/search": "6.7.1", + "@codemirror/state": "6.7.1", + "@codemirror/theme-one-dark": "6.1.3", + "@codemirror/view": "6.43.7", + "@lezer/highlight": "1.2.3", + "@wailsio/runtime": "3.0.0-alpha.74", + "codemirror": "6.0.2" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.1", - "svelte": "^4.2.8", - "vite": "^5.0.8" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" + "@sveltejs/vite-plugin-svelte": "7.2.0", + "svelte": "5.56.8", + "vite": "8.2.0" } }, "node_modules/@codemirror/autocomplete": { - "version": "6.20.1", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz", - "integrity": "sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==", - "license": "MIT", + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -53,22 +38,20 @@ } }, "node_modules/@codemirror/commands": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", - "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", - "license": "MIT", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.4.tgz", + "integrity": "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==", "dependencies": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.6.0", + "@codemirror/state": "^6.7.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "node_modules/@codemirror/language": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", - "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", - "license": "MIT", + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", + "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -79,21 +62,19 @@ } }, "node_modules/@codemirror/lint": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.5.tgz", - "integrity": "sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==", - "license": "MIT", + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", + "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.35.0", + "@codemirror/view": "^6.42.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/search": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.6.0.tgz", - "integrity": "sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw==", - "license": "MIT", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz", + "integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.37.0", @@ -101,10 +82,9 @@ } }, "node_modules/@codemirror/state": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", - "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", - "license": "MIT", + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.1.tgz", + "integrity": "sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" } @@ -122,425 +102,41 @@ } }, "node_modules/@codemirror/view": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.41.0.tgz", - "integrity": "sha512-6H/qadXsVuDY219Yljhohglve8xf4B8xJkVOEWfA5uiYKiTFppjqsvsfR5iPA0RbvRBoOyTZpbLIxe9+0UR8xA==", - "license": "MIT", + "version": "6.43.7", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.7.tgz", + "integrity": "sha512-FZsExxkoxnAN+d9TgqXLg5g4A1oQwzX9WlkOT5i2PKkcW7xx3Bmu0vs90g6fo9Mpdsb/l96dnAraQ8932aO4/g==", "dependencies": { - "@codemirror/state": "^6.6.0", + "@codemirror/state": "^6.7.0", "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -549,15 +145,13 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -593,417 +187,295 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", - "cpu": [ - "arm" - ], + "node_modules/@oxc-project/types": { + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "funding": { + "url": "https://github.com/sponsors/Boshen" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.2.tgz", + "integrity": "sha512-l7x215OGvo1s52JWmR8U/DAVzEDWBCIbTm28aeJV/WDTSHgcKXaZTuBT0hJMs5NggilfJTW3clZVvd24yfKJxA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.2.tgz", + "integrity": "sha512-9u9Xv6c1AJZT0FfwH5vrMG5Jjcwhc1MlyrPu0XfTqkzsmqfks2M6W/o5XwAJgVVN/jHpqqngC1WevHKKTIUtIA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.2.tgz", + "integrity": "sha512-9W1mbGZAfW3oqd85bhBkmpyHCCzL1TeG/zFFP3vg7b0rlly8cxOcre5nXwz+LHazCwac2MNWgPdPCHndABjpWQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.2.tgz", + "integrity": "sha512-0p1lhiCSCyaerFwtrdZQUx7NqGk6LQnaRKWX7tFQqwQgvX0rjM15cIkm3pax1UpEakK14C4mOxx/jSqCBdBRqQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.2.tgz", + "integrity": "sha512-e+cOJXrJ2L3zx6YzqPg+f6Wbk3V1cKB8bOhbaYdVYN3DdquzNdRAmrbETz1qnt5yp/c7JNlNjmITiA2cVneQ7w==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.2.tgz", + "integrity": "sha512-JsSMsj6sNat/MuhG5fnBD7QgbtpHKVe30x5/bAVirDHdhoQRXJkF6xc0Jqk8O4fiCUQAzMOoH9wZi3m60c8wtg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.2.tgz", + "integrity": "sha512-B5G/zJdHaoJn9vD50eGHWkiWfmq8Uhi3IiLPJTzmZTrAalk1bztUikSXo0qga18ibE0IXboyeMUnhPjhAJ45wQ==", "cpu": [ - "loong64" + "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.2.tgz", + "integrity": "sha512-6mC/awzKka8W6EoekjegpfGkjz8jXWDX63pqu/HYVpyKtZfu65Jsh4QAH3Kej3CAv/c1oGX7psTmFEbr0mDxLA==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.2.tgz", + "integrity": "sha512-412MX9fJLdA1IK28EZnc8jYv2HRTleOZgfLQumJ5zy7OeJLZlg/CETwFaXjNmGVxG51cFHpKLqb5LKvBC+HsHA==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.2.tgz", + "integrity": "sha512-Q/+HI/ToJafZ1iCqGgVQXUEkIjufHCTF0gBQ2a5o3cg7GJ2h0qyq3nvvSmU+bGda2/7ygXpTY4TM6gO9OhQ0ZA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.2.tgz", + "integrity": "sha512-ZKp/w41n6wCvxzxQHtQSbuphfX3Y4cCvbjkKHusrLx4lh+JWLTU7StSltO/DKARISzbj368d+qUaCjI8K2wzXw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.2.tgz", + "integrity": "sha512-pxE6xD4KS3eAROkKK5yrhB9/3+vhlhVGMvlQLbdpzrBGDbKrnzx3RLwPaHvasLo6jgaiBLn7e04Df9C4tYhjmA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.2.tgz", + "integrity": "sha512-4MqEue5re+xIZzAWsB8sj0P1kqZySWqIuN4t6QaIO/YA6SFwySOLruvWQFzfmqk8LBK2P30KCSJwf6mJCZZ5/A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.2.tgz", + "integrity": "sha512-NweNxxD0Nf9t8v7kodun45Ijp3EIwYY+uydPP6qBEYvfBqhIjN6dZMzlQja3tqX/aLs3F3Uz+AxDpKgRhpOZQg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@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" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", - "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.11.tgz", + "integrity": "sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", - "debug": "^4.3.4", - "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.10", - "svelte-hmr": "^0.16.0", - "vitefu": "^0.2.5" - }, - "engines": { - "node": "^18.0.0 || >=20" - }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "acorn": "^8.9.0" } }, - "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", - "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.2.0.tgz", + "integrity": "sha512-1SpkuMSRLfugrVX+IrKfE1RUegzo8AQzKQ6qQPfVzbcWi5IhuTPaKb5ZrLpucleFznkc4/RTeSPoRnGWFxX+EQ==", "dev": true, - "license": "MIT", "dependencies": { - "debug": "^4.3.4" + "deepmerge": "^4.3.1", + "magic-string": "^0.30.21", + "obug": "^2.1.0", + "vitefu": "^1.1.2" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^20.19 || ^22.12 || >=24" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true }, "node_modules/@wailsio/runtime": { - "version": "3.0.0-alpha.79", - "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.79.tgz", - "integrity": "sha512-NITzxKmJsMEruc39L166lbPJVECxzcbdqpHVqOOF7Cu/7Zqk/e3B/gNpkUjhNyo5rVb3V1wpS8oEgLUmpu1cwA==", - "license": "MIT" + "version": "3.0.0-alpha.74", + "resolved": "https://registry.npmjs.org/@wailsio/runtime/-/runtime-3.0.0-alpha.74.tgz", + "integrity": "sha512-6N3F6MpLDgLfTRIwgwAzxSrIVtlPICxMYDrs0bz5uUJ58IPCQjcqxWOedoMysFdqVaogi5VmCxXLKRJzI5hW2A==" }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -1012,11 +484,10 @@ } }, "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -1026,23 +497,17 @@ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" + "engines": { + "node": ">=6" } }, "node_modules/codemirror": { @@ -1066,95 +531,69 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "license": "MIT" }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + "node": ">=0.10.0" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", + "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==", + "dev": true + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true + }, + "node_modules/esrap": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.3.0.tgz", + "integrity": "sha512-GQ/7RN8uOtEfNpzZzBMTzW9JBcX42oaSVtPzdF+6cEL8pqIL094iUpr9jzYGn4O4P/1S60dJ6izyT8F4LYARng==", "dev": true, - "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@jridgewell/sourcemap-codec": "^1.4.15" }, - "engines": { - "node": ">=6.0" + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" }, "peerDependenciesMeta": { - "supports-color": { + "@typescript-eslint/types": { "optional": true } } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "node": ">=12.0.0" }, - "engines": { - "node": ">=12" + "peerDependencies": { + "picomatch": "^3 || ^4" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/fsevents": { @@ -1163,7 +602,6 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1177,56 +615,278 @@ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "^1.0.6" } }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, - "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.3" + }, "engines": { - "node": ">=6" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "dev": true, "funding": [ { @@ -1234,7 +894,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1242,29 +901,41 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "node_modules/obug": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "engines": { + "node": ">=12.20.0" } }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, - "license": "ISC" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -1280,9 +951,8 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -1290,49 +960,36 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "node_modules/rolldown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.2.tgz", + "integrity": "sha512-opwpo1tQBAcpSUJDt94B7hhLNGOKjCdE//XXjeLrnx9b83bjnw45tXdg1b09yEw/VLFBJGZpwRULMmOZo7ol+A==", "dev": true, - "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/types": "=0.142.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.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" + "@rolldown/binding-android-arm64": "1.2.2", + "@rolldown/binding-darwin-arm64": "1.2.2", + "@rolldown/binding-darwin-x64": "1.2.2", + "@rolldown/binding-freebsd-x64": "1.2.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.2", + "@rolldown/binding-linux-arm64-gnu": "1.2.2", + "@rolldown/binding-linux-arm64-musl": "1.2.2", + "@rolldown/binding-linux-ppc64-gnu": "1.2.2", + "@rolldown/binding-linux-s390x-gnu": "1.2.2", + "@rolldown/binding-linux-x64-gnu": "1.2.2", + "@rolldown/binding-linux-x64-musl": "1.2.2", + "@rolldown/binding-openharmony-arm64": "1.2.2", + "@rolldown/binding-win32-arm64-msvc": "1.2.2", + "@rolldown/binding-win32-x64-msvc": "1.2.2" } }, "node_modules/source-map-js": { @@ -1340,7 +997,6 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -1352,62 +1008,65 @@ "license": "MIT" }, "node_modules/svelte": { - "version": "4.2.20", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.20.tgz", - "integrity": "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==", + "version": "5.56.8", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.8.tgz", + "integrity": "sha512-PY8LOw7xP6c8IOiVqdo0sbbZVYhXRSfklOQLAUyGBKqjTX0wx/z4l/9J+PmBpmlLnxzEb1NqltxQ5/wZme/Cmg==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.10", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.12", + "is-reference": "^3.0.3", "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" }, "engines": { - "node": ">=16" + "node": ">=18" } }, - "node_modules/svelte-hmr": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", - "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, - "license": "ISC", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" + "node": ">=12.0.0" }, - "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/vite": { - "version": "5.4.21", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", - "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", "dev": true, - "license": "MIT", - "peer": true, "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -1416,23 +1075,33 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "less": { + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -1449,17 +1118,22 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", "dev": true, - "license": "MIT", "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "vite": { @@ -1472,6 +1146,12 @@ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", "license": "MIT" + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true } } } diff --git a/frontend/package.json b/frontend/package.json index 1762eb3..249de53 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,21 +10,21 @@ "preview": "vite preview" }, "dependencies": { - "@codemirror/autocomplete": "^6.20.1", - "@codemirror/commands": "^6.10.3", - "@codemirror/language": "^6.12.3", - "@codemirror/lint": "^6.9.5", - "@codemirror/search": "^6.6.0", - "@codemirror/state": "^6.6.0", - "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.41.0", - "@lezer/highlight": "^1.2.3", - "@wailsio/runtime": "latest", - "codemirror": "^6.0.2" + "@codemirror/autocomplete": "6.20.3", + "@codemirror/commands": "6.10.4", + "@codemirror/language": "6.12.4", + "@codemirror/lint": "6.9.7", + "@codemirror/search": "6.7.1", + "@codemirror/state": "6.7.1", + "@codemirror/theme-one-dark": "6.1.3", + "@codemirror/view": "6.43.7", + "@lezer/highlight": "1.2.3", + "@wailsio/runtime": "3.0.0-alpha.74", + "codemirror": "6.0.2" }, "devDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.1", - "svelte": "^4.2.8", - "vite": "^5.0.8" + "@sveltejs/vite-plugin-svelte": "7.2.0", + "svelte": "5.56.8", + "vite": "8.2.0" } } diff --git a/frontend/public/style.css b/frontend/public/style.css index 57a3014..b73acdd 100644 --- a/frontend/public/style.css +++ b/frontend/public/style.css @@ -114,9 +114,14 @@ /* ============ Non-color tokens (shared across themes) ============ */ - /* Typography: macOS system font stack — SF Pro Text/Display auto-selected */ + /* Native UI font stack. Keep each platform's normal system face while + * giving Linux an explicit metric-compatible choice and Korean glyphs. + * A bare sans-serif fallback resolved to DejaVu Sans on Raspberry Pi OS, + * whose button baselines sit visibly high compared with SF/Segoe UI. */ --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display", - "Helvetica Neue", Helvetica, Arial, sans-serif; + "Segoe UI", "Apple SD Gothic Neo", "Malgun Gothic", + "Cantarell", "Noto Sans", "Noto Sans CJK KR", "Helvetica Neue", Helvetica, + Arial, "Liberation Sans", sans-serif; --font-mono: ui-monospace, "SF Mono", Menlo, Monaco, "Cascadia Mono", Consolas, monospace; diff --git a/frontend/src/main.js b/frontend/src/main.js index fb36356..c4258de 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,6 +1,7 @@ import App from './App.svelte' +import { mount } from 'svelte' -const app = new App({ +const app = mount(App, { target: document.getElementById('app'), }) diff --git a/frontend/vite.config.js b/frontend/vite.config.js index ca0dde4..b13e9ed 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -3,54 +3,16 @@ import { svelte } from "@sveltejs/vite-plugin-svelte"; import wails from "@wailsio/runtime/plugins/vite"; // https://vitejs.dev/config/ -// -// Bundle split policy: the previous single 531 KB chunk delays cold start -// because the whole app (Settings, LogViewer, TunnelDetail, KeyGenerator) -// has to parse before the first paint. We extract each modal/heavy -// component into its own chunk so the initial render only needs the -// tunnel-list path; modals load on demand when the user opens them. export default defineConfig({ plugins: [svelte(), wails("./bindings")], build: { - // Bump chunk size warning ceiling because the runtime+Svelte combined - // chunk is still ~300 KB even after splitting — that's expected for a - // Svelte+Wails app and not actionable. + // WebKitGTK versions shipped by supported Linux distributions lag the + // evergreen browsers Vite targets by default. Transpile modern syntax so + // the application mounts on those embedded WebKit runtimes as well. + target: "safari13", + // Let Rolldown derive safe chunk boundaries. The previous forced chunk + // graph created circular startup imports under Vite 8 and left WebKitGTK + // with a blank window before the Svelte application could mount. chunkSizeWarningLimit: 600, - rollupOptions: { - output: { - manualChunks: { - // Wails bindings + runtime — heavy and rarely changes; cache - // separately so app code updates don't bust this chunk. - "wails-runtime": ["@wailsio/runtime"], - // CodeMirror — ~250 KB just for the editor framework + every - // language pack we import. ConfigEditor is the only consumer - // today but isolating CodeMirror into its own chunk means the - // editor view shows the chrome immediately and the heavy - // syntax-highlight code streams in next, instead of blocking - // the first paint. - "codemirror": [ - "@codemirror/autocomplete", - "@codemirror/commands", - "@codemirror/language", - "@codemirror/lint", - "@codemirror/search", - "@codemirror/state", - "@codemirror/view", - ], - // Heavy modals — loaded only when the user opens them. Keeping - // them out of the main chunk shaves ~150 KB off the initial - // load. - "modal-logs": ["./src/lib/LogViewer.svelte"], - "modal-settings": ["./src/lib/Settings.svelte"], - "modal-tunnel-detail": ["./src/lib/TunnelDetail.svelte"], - "modal-config-editor": ["./src/lib/ConfigEditor.svelte"], - "modal-keygen": ["./src/lib/KeyGenerator.svelte"], - "modal-history": ["./src/lib/History.svelte"], - "modal-route-viz": ["./src/lib/RouteVisualization.svelte"], - "modal-stats": ["./src/lib/StatsDashboard.svelte"], - "modal-dnsleak": ["./src/lib/DNSLeakTest.svelte"], - }, - }, - }, }, }); diff --git a/go.mod b/go.mod index 7d4927c..0c18882 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module github.com/korjwl1/wireguide -go 1.25.0 +go 1.25.12 require ( github.com/Microsoft/go-winio v0.6.2 github.com/godbus/dbus/v5 v5.2.2 github.com/makiuchi-d/gozxing v0.1.1 github.com/wailsapp/wails/v3 v3.0.0-alpha.74 - golang.org/x/crypto v0.49.0 - golang.org/x/image v0.35.0 - golang.org/x/sys v0.42.0 - golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb + golang.org/x/crypto v0.54.0 + golang.org/x/image v0.44.0 + golang.org/x/sys v0.47.0 + golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 ) @@ -25,8 +25,8 @@ require ( github.com/ebitengine/purego v0.9.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.7.0 // indirect - github.com/go-git/go-git/v5 v5.16.4 // indirect + github.com/go-git/go-billy/v5 v5.9.0 // indirect + github.com/go-git/go-git/v5 v5.19.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/go-cmp v0.7.0 // indirect @@ -44,7 +44,7 @@ require ( github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.5.1 // indirect - github.com/pjbgf/sha1cd v0.5.0 // indirect + github.com/pjbgf/sha1cd v0.6.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/samber/lo v1.52.0 // indirect @@ -52,9 +52,9 @@ require ( github.com/skeema/knownhosts v1.3.2 // indirect github.com/wailsapp/go-webview2 v1.0.23 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/net v0.51.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/text v0.40.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index fe66fb5..8ebfa0a 100644 --- a/go.sum +++ b/go.sum @@ -32,12 +32,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= -github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y= -github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= +github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e h1:Lf/gRkoycfOBPa42vU2bbgPurFong6zXeFtPoxholzU= github.com/go-json-experiment/json v0.0.0-20251027170946-4849db3c2f7e/go.mod h1:uNVvRXArCGbZ508SxYYTC5v1JWoz2voff5pm25jU1Ok= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= @@ -94,8 +94,8 @@ github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE9 github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= -github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= +github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= +github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -126,17 +126,17 @@ github.com/wailsapp/wails/v3 v3.0.0-alpha.74/go.mod h1:4saK4A4K9970X+X7RkMwP2lyG github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= -golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= -golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= -golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= +golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -146,14 +146,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -161,8 +161,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= -golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= -golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= +golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446 h1:cqHQ3AycTHvM2R7ikgyX57D+XvtcSnGylsLkOVhta/w= +golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 h1:3GDAcqdIg1ozBNLgPy4SLT84nfcBjr6rhGtXYtrkWLU= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/gui/dock_other.go b/internal/gui/dock_other.go index 4fd6fb1..857878b 100644 --- a/internal/gui/dock_other.go +++ b/internal/gui/dock_other.go @@ -2,7 +2,11 @@ package gui -import "github.com/wailsapp/wails/v3/pkg/application" +import ( + "runtime" + + "github.com/wailsapp/wails/v3/pkg/application" +) var dockWindow *application.WebviewWindow @@ -20,6 +24,12 @@ func showDock() { dockWindow.Restore() } dockWindow.Show() + if runtime.GOOS == "linux" { + // labwc/XWayland may drop server-side decorations when a GTK window is + // hidden and mapped again. Reasserting the decorated state after Show + // refreshes the WM hint and restores the title bar. + dockWindow.SetFrameless(false) + } dockWindow.Focus() } diff --git a/internal/gui/gui.go b/internal/gui/gui.go index 992e4a5..4519296 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -191,7 +191,16 @@ func Run(assetsHandler http.Handler, dataDir string) error { // listener races with Wails' listener — the window gets destroyed despite // Cancel. Hooks run sequentially BEFORE listeners, so Cancel() here // reliably prevents Wails' default close/destroy behavior. - win.RegisterHook(events.Common.WindowClosing, func(event *application.WindowEvent) { + // Linux must intercept the native delete event itself. Wails maps + // Linux.WindowDeleteEvent to Common.WindowClosing asynchronously; hooking + // only the mapped event allows the default Common.WindowClosing listener to + // destroy the window before close-to-tray can cancel it. Windows and macOS + // expose a cancellable Common.WindowClosing event directly. + closingEvent := events.Common.WindowClosing + if runtime.GOOS == "linux" { + closingEvent = events.Linux.WindowDeleteEvent + } + win.RegisterHook(closingEvent, func(event *application.WindowEvent) { event.Cancel() win.Hide() hideDock() @@ -219,19 +228,18 @@ func Run(assetsHandler http.Handler, dataDir string) error { tray.SetIcon(trayOffIconDark) } else { tray.SetLabel("WireGuide") - // Windows also needs an explicit SetIcon at init or Wails falls - // back to the embedded white-W template — setIconState only - // runs on connect/disconnect transitions, so a fresh launch - // with no tunnel previously never showed our rounded icon. - if runtime.GOOS == "windows" && len(trayOffIconWindows) > 0 { + // Windows and Linux need an explicit icon at startup. Without it, + // Linux StatusNotifier hosts display their own fallback icon and + // Windows falls back to Wails' embedded template. setIconState only + // runs on connection transitions, so it cannot initialise the icon. + if (runtime.GOOS == "windows" || runtime.GOOS == "linux") && len(trayOffIconWindows) > 0 { tray.SetIcon(trayOffIconWindows) } - // Windows convention: left-click on a tray icon is the primary - // action — show the main window (WireGuard-for-Windows, Discord, - // Slack all behave this way); the menu stays on right-click. + // Windows and Linux convention: left-click is the primary action + // and shows the main window; the context menu remains on right-click. // Registered only here: on macOS any click opens the NSStatusItem // menu natively, and an OnClick handler would fight it. - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "linux" { tray.OnClick(showDock) } } diff --git a/internal/gui/tray.go b/internal/gui/tray.go index e907261..b299fa4 100644 --- a/internal/gui/tray.go +++ b/internal/gui/tray.go @@ -485,10 +485,9 @@ func (t *trayManager) setIconState(activeNames []string, handshakeMap map[string if activeChanged { onIcon, offIcon := t.macIcons() - if runtime.GOOS == "windows" && len(trayOnIconWindows) > 0 { - // Use the rounded-red app-icon variants on Windows so the - // tray icon actually stands out against a light system-tray - // background and isn't framed by a white square. + if (runtime.GOOS == "windows" || runtime.GOOS == "linux") && len(trayOnIconWindows) > 0 { + // Use the rounded-red app-icon variants on Windows and Linux so + // the tray icon stands out against light system-tray backgrounds. onIcon, offIcon = trayOnIconWindows, trayOffIconWindows } if anyConnected { @@ -496,7 +495,7 @@ func (t *trayManager) setIconState(activeNames []string, handshakeMap map[string tooltip := "WireGuide — " + strings.Join(activeNames, ", ") t.tray.SetTooltip(tooltip) } else { - if runtime.GOOS == "darwin" || (runtime.GOOS == "windows" && len(offIcon) > 0) { + if runtime.GOOS == "darwin" || ((runtime.GOOS == "windows" || runtime.GOOS == "linux") && len(offIcon) > 0) { t.tray.SetIcon(offIcon) } t.tray.SetTooltip("WireGuide") diff --git a/internal/ipc/ipc_test.go b/internal/ipc/ipc_test.go index 7fd42ff..5aed1ab 100644 --- a/internal/ipc/ipc_test.go +++ b/internal/ipc/ipc_test.go @@ -13,7 +13,10 @@ func testSocketPath(t *testing.T) string { if runtime.GOOS == "windows" { return `\\.\pipe\wireguide-test-` + t.Name() } - return filepath.Join(os.TempDir(), "wireguide-test-"+t.Name()+".sock") + // Listen deliberately rejects sockets placed directly in a directory + // owned by another UID. /tmp is normally root-owned on Linux, so give + // each test a private, test-user-owned parent directory instead. + return filepath.Join(t.TempDir(), "wireguide.sock") } // registerTestPing registers a Helper.Ping handler that returns the current diff --git a/internal/reconnect/network_linux.go b/internal/reconnect/network_linux.go index 9d0e0be..5bf624b 100644 --- a/internal/reconnect/network_linux.go +++ b/internal/reconnect/network_linux.go @@ -3,29 +3,31 @@ package reconnect import ( + "bufio" + "fmt" + "io" "log/slog" + "os" + "strconv" + "strings" "sync" "syscall" "golang.org/x/sys/unix" ) -// linuxNetworkChangeDetector subscribes to the kernel's RTNETLINK multicast -// groups for route, address, and link transitions on the underlying -// interfaces. When a relevant event fires we coalesce a burst into a single -// notification on ChangeChan (500ms debounce). The reconnect monitor then -// triggers a per-tunnel reconnect, instead of waiting up to 40s for the -// generic sleep/wake heuristic to notice. -// -// We DO NOT use NetworkManager DBus here — that would tie us to NM, which -// not every distro ships (Alpine/Arch headless). Raw netlink works on any -// kernel with CONFIG_RTNETLINK (i.e. every modern Linux). +// linuxNetworkChangeDetector uses RTNETLINK only as a wake-up source. A +// notification is emitted only when the actual non-tunnel default route +// changes. Address and link chatter is common on Linux (NetworkManager, +// mDNS, IPv6 temporary addresses, and our own TUN setup all generate it), so +// treating every netlink message as an upstream change causes reconnect loops. type linuxNetworkChangeDetector struct { - mu sync.Mutex - fd int - stopCh chan struct{} - changeCh chan struct{} - running bool + mu sync.Mutex + fd int + stopCh chan struct{} + changeCh chan struct{} + running bool + lastSnapshot string } func NewNetworkChangeDetector() NetworkChangeDetector { @@ -40,6 +42,7 @@ func (d *linuxNetworkChangeDetector) Start() { } d.stopCh = make(chan struct{}) d.changeCh = make(chan struct{}, 1) + d.lastSnapshot = defaultRouteSnapshot() d.running = true d.mu.Unlock() @@ -69,7 +72,7 @@ func (d *linuxNetworkChangeDetector) Start() { d.fd = fd d.mu.Unlock() go d.readLoop() - slog.Info("netlink network-change detector started") + slog.Info("netlink network-change detector started", "default_route", d.lastSnapshot) } func (d *linuxNetworkChangeDetector) Stop() { @@ -89,33 +92,36 @@ func (d *linuxNetworkChangeDetector) Stop() { close(stop) } if fd != 0 { - // Shutdown unblocks the recvfrom in readLoop. _ = unix.Shutdown(fd, unix.SHUT_RDWR) _ = unix.Close(fd) } } -func (d *linuxNetworkChangeDetector) ChangeChan() <-chan struct{} { - return d.changeCh +func (d *linuxNetworkChangeDetector) ChangeChan() <-chan struct{} { return d.changeCh } + +func (d *linuxNetworkChangeDetector) checkDefaultRoute() { + now := defaultRouteSnapshot() + d.mu.Lock() + previous := d.lastSnapshot + if now == previous { + d.mu.Unlock() + return + } + d.lastSnapshot = now + d.mu.Unlock() + + slog.Info("network primary upstream changed", "previous", previous, "now", now) + select { + case d.changeCh <- struct{}{}: + default: + } } -// readLoop drains netlink messages. Every message we receive that isn't a -// trivial NLMSG_DONE/NLMSG_ERROR is treated as "topology changed" — we don't -// try to filter by message type because any of our subscribed groups -// firing implies a route/address/link change worth re-checking. -// -// Error handling per man netlink(7): -// - EINTR / EAGAIN / EWOULDBLOCK: transient; keep going. -// - ENOBUFS: kernel ran out of receive buffer and dropped messages — -// we may have missed an RTM event. Fire a single "force" signal so -// the reconnect monitor re-evaluates, then continue reading. -// - Anything else (EBADF on shutdown, etc.): exit cleanly. func (d *linuxNetworkChangeDetector) readLoop() { buf := make([]byte, 8192) for { d.mu.Lock() - running := d.running - fd := d.fd + running, fd := d.running, d.fd d.mu.Unlock() if !running { return @@ -123,18 +129,13 @@ func (d *linuxNetworkChangeDetector) readLoop() { n, _, err := unix.Recvfrom(fd, buf, 0) if err != nil { switch err { - // EAGAIN == EWOULDBLOCK on Linux; listing one is enough. case syscall.EINTR, syscall.EAGAIN: continue case syscall.ENOBUFS: - slog.Warn("netlink ENOBUFS — kernel dropped messages, forcing reconnect check") - select { - case d.changeCh <- struct{}{}: - default: - } + slog.Warn("netlink ENOBUFS; rechecking the default route") + d.checkDefaultRoute() continue } - // EBADF / ENOTCONN are expected during shutdown. d.mu.Lock() stillRunning := d.running d.mu.Unlock() @@ -143,22 +144,93 @@ func (d *linuxNetworkChangeDetector) readLoop() { } return } - if n <= 0 { + if n > 0 { + d.checkDefaultRoute() + } + } +} + +func defaultRouteSnapshot() string { + v4, err4 := os.Open("/proc/net/route") + if err4 == nil { + defer v4.Close() + } + v6, err6 := os.Open("/proc/net/ipv6_route") + if err6 == nil { + defer v6.Close() + } + return routeSnapshot(v4, v6, func(iface string) bool { + if iface == "lo" { + return true + } + _, err := os.Stat("/sys/class/net/" + iface + "/tun_flags") + return err == nil + }) +} + +type defaultRoute struct { + iface string + gateway string + metric uint64 +} + +func routeSnapshot(v4, v6 io.Reader, isTunnel func(string) bool) string { + parts := make([]string, 0, 2) + if route, ok := bestIPv4Default(v4, isTunnel); ok { + parts = append(parts, fmt.Sprintf("v4:%s:%s:%d", route.iface, route.gateway, route.metric)) + } + if route, ok := bestIPv6Default(v6, isTunnel); ok { + parts = append(parts, fmt.Sprintf("v6:%s:%s:%d", route.iface, route.gateway, route.metric)) + } + return strings.Join(parts, "|") +} + +func bestIPv4Default(r io.Reader, isTunnel func(string) bool) (defaultRoute, bool) { + var best defaultRoute + found := false + if r == nil { + return best, false + } + s := bufio.NewScanner(r) + for s.Scan() { + f := strings.Fields(s.Text()) + if len(f) < 8 || f[1] != "00000000" || f[7] != "00000000" || isTunnel(f[0]) { + continue + } + flags, err1 := strconv.ParseUint(f[3], 16, 64) + metric, err2 := strconv.ParseUint(f[6], 10, 64) + if err1 != nil || err2 != nil || flags&unix.RTF_UP == 0 { continue } - // Signal the debouncer non-blockingly. - select { - case d.changeCh <- struct{}{}: - default: - // Coalesce: a notification is already pending. + candidate := defaultRoute{iface: f[0], gateway: f[2], metric: metric} + if !found || candidate.metric < best.metric { + best, found = candidate, true } } + return best, found } -// Note on coalescing: the readLoop sends directly to changeCh (cap 1) -// non-blockingly. If multiple RTM messages arrive in quick succession -// only the first one is delivered until a consumer drains the channel — -// the cap-1 buffer already implements "edge-triggered, single pending -// notification" coalescing. The reconnect monitor downstream runs each -// reconnect under its own backoff, so a separate debouncer goroutine -// inside this detector adds no value. +func bestIPv6Default(r io.Reader, isTunnel func(string) bool) (defaultRoute, bool) { + var best defaultRoute + found := false + if r == nil { + return best, false + } + s := bufio.NewScanner(r) + for s.Scan() { + f := strings.Fields(s.Text()) + if len(f) < 10 || f[0] != strings.Repeat("0", 32) || f[1] != "00" || f[2] != strings.Repeat("0", 32) || f[3] != "00" || isTunnel(f[9]) { + continue + } + metric, err := strconv.ParseUint(f[5], 16, 64) + flags, flagsErr := strconv.ParseUint(f[8], 16, 64) + if err != nil || flagsErr != nil || flags&unix.RTF_UP == 0 { + continue + } + candidate := defaultRoute{iface: f[9], gateway: f[4], metric: metric} + if !found || candidate.metric < best.metric { + best, found = candidate, true + } + } + return best, found +} diff --git a/internal/reconnect/network_linux_test.go b/internal/reconnect/network_linux_test.go new file mode 100644 index 0000000..faf565b --- /dev/null +++ b/internal/reconnect/network_linux_test.go @@ -0,0 +1,43 @@ +//go:build linux + +package reconnect + +import ( + "strings" + "testing" +) + +func TestRouteSnapshotIgnoresNoiseAndTunnelDefaults(t *testing.T) { + v4 := `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT +wlan0 00000000 0101A8C0 0003 0 0 600 00000000 0 0 0 +wg-demo 00000000 00000000 0001 0 0 0 00000000 0 0 0 +wlan0 0001A8C0 00000000 0001 0 0 600 00FFFFFF 0 0 0 +` + v6 := `00000000000000000000000000000000 00 00000000000000000000000000000000 00 FE800000000000000001000000000001 00000400 00000000 00000000 00000003 wlan0 +00000000000000000000000000000000 00 00000000000000000000000000000000 00 00000000000000000000000000000000 00000000 00000000 00000000 00000001 wg-demo +` + isTunnel := func(name string) bool { return name == "wg-demo" } + got := routeSnapshot(strings.NewReader(v4), strings.NewReader(v6), isTunnel) + want := "v4:wlan0:0101A8C0:600|v6:wlan0:FE800000000000000001000000000001:1024" + if got != want { + t.Fatalf("routeSnapshot() = %q, want %q", got, want) + } +} + +func TestRouteSnapshotChangesOnlyForDefaultRouteState(t *testing.T) { + base := `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT +wlan0 00000000 0101A8C0 0003 0 0 600 00000000 0 0 0 +` + withAddressRouteNoise := base + "wlan0 0001A8C0 00000000 0001 0 0 600 00FFFFFF 0 0 0\n" + changedGateway := strings.Replace(base, "0101A8C0", "FE01A8C0", 1) + none := func(string) bool { return false } + a := routeSnapshot(strings.NewReader(base), nil, none) + b := routeSnapshot(strings.NewReader(withAddressRouteNoise), nil, none) + c := routeSnapshot(strings.NewReader(changedGateway), nil, none) + if a != b { + t.Fatalf("non-default route noise changed snapshot: %q != %q", a, b) + } + if a == c { + t.Fatalf("gateway change did not change snapshot: %q", a) + } +} From 8f8c34c38bcc3f507c40ad47389ef1130bce2362 Mon Sep 17 00:00:00 2001 From: korjwl1 <72062804+korjwl1@users.noreply.github.com> Date: Tue, 4 Aug 2026 01:37:37 +0900 Subject: [PATCH 03/13] fix Linux window decorations after tray restore --- internal/gui/dock_other.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/gui/dock_other.go b/internal/gui/dock_other.go index 857878b..d0ed126 100644 --- a/internal/gui/dock_other.go +++ b/internal/gui/dock_other.go @@ -23,13 +23,16 @@ func showDock() { if dockWindow.IsMinimised() { dockWindow.Restore() } - dockWindow.Show() if runtime.GOOS == "linux" { - // labwc/XWayland may drop server-side decorations when a GTK window is - // hidden and mapped again. Reasserting the decorated state after Show - // refreshes the WM hint and restores the title bar. + // labwc/XWayland may forget server-side decorations when a hidden GTK + // window is mapped again. Merely setting decorated=true is ineffective + // because GTK already caches that value and sends no new WM hint. Toggle + // it while the window is hidden so the next map is unambiguously + // decorated, without exposing an intermediate frameless frame. + dockWindow.SetFrameless(true) dockWindow.SetFrameless(false) } + dockWindow.Show() dockWindow.Focus() } From 9c73dd2498044c406125c92ac7515159220e69f0 Mon Sep 17 00:00:00 2001 From: korjwl1 Date: Wed, 5 Aug 2026 18:09:25 +0900 Subject: [PATCH 04/13] fix(frontend): Svelte 5 drop-overlay regression; dead code purge; render perf (Refs #35) - Restore file-drop overlay: Svelte 5 prunes child-combinator rules with runtime-added ancestor classes; use descendant combinator - Remove dead CSS (import-modal remnants in App.svelte, use-current-network remnants in AutomationEditor) - Remove 4 never-imported components (MiniMode, SplitTunnelUI, ScriptWarning, KeyGenerator) and unused tPlain translator - Prune 100 unused i18n keys per locale (en/ko/ja), all statically verified - Gate store notifications in stores/tunnels.js: Svelte object stores re-notify unconditionally, causing full re-sorts and reactive recompute at 1 Hz while idle - Key LogViewer rows by monotonic id (ring-buffer wrap rewrote all 1000 rows per record) and TunnelList rows by name - Settings: carry trusted_ssids from fresh fetch (lost-update with CLI) - Drop frontend/frontend/bindings stubs committed by accident in bootstrap Co-Authored-By: Claude Fable 5 --- .../wailsapp/wails/v3/internal/eventcreate.js | 9 -- .../wailsapp/wails/v3/internal/eventdata.d.ts | 2 - frontend/src/App.svelte | 108 +------------ frontend/src/i18n/en.json | 124 +-------------- frontend/src/i18n/index.js | 8 - frontend/src/i18n/ja.json | 124 +-------------- frontend/src/i18n/ko.json | 124 +-------------- frontend/src/lib/AutomationEditor.svelte | 9 -- frontend/src/lib/KeyGenerator.svelte | 102 ------------ frontend/src/lib/LogViewer.svelte | 2 +- frontend/src/lib/MiniMode.svelte | 115 -------------- frontend/src/lib/ScriptWarning.svelte | 123 --------------- frontend/src/lib/Settings.svelte | 21 +-- frontend/src/lib/SplitTunnelUI.svelte | 149 ------------------ frontend/src/lib/TunnelList.svelte | 2 +- frontend/src/stores/logs.js | 4 + frontend/src/stores/tunnels.js | 48 ++++-- 17 files changed, 59 insertions(+), 1015 deletions(-) delete mode 100644 frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js delete mode 100644 frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts delete mode 100644 frontend/src/lib/KeyGenerator.svelte delete mode 100644 frontend/src/lib/MiniMode.svelte delete mode 100644 frontend/src/lib/ScriptWarning.svelte delete mode 100644 frontend/src/lib/SplitTunnelUI.svelte diff --git a/frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js b/frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js deleted file mode 100644 index 1ea1058..0000000 --- a/frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventcreate.js +++ /dev/null @@ -1,9 +0,0 @@ -//@ts-check -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore: Unused imports -import { Create as $Create } from "@wailsio/runtime"; - -Object.freeze($Create.Events); diff --git a/frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts b/frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts deleted file mode 100644 index 3dd1807..0000000 --- a/frontend/frontend/bindings/github.com/wailsapp/wails/v3/internal/eventdata.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL -// This file is automatically generated. DO NOT EDIT diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 11d32a0..a27b3a8 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -920,7 +920,10 @@ @media (prefers-reduced-motion: no-preference) { .drop-overlay { transition: opacity var(--dur-base) var(--ease-out); } } - :global(.file-drop-target-active) > .drop-overlay { + /* Descendant (not child >) combinator: Svelte 5 prunes child-combinator + * rules whose parent class it can't statically prove, and Wails adds + * .file-drop-target-active at runtime — a `>` here ships commented-out. */ + :global(.file-drop-target-active) .drop-overlay { opacity: 1; visibility: visible; backdrop-filter: blur(10px) saturate(180%); @@ -1451,107 +1454,4 @@ color: var(--text-primary); font: var(--text-title-2); } - .modal label { - display: block; - margin: var(--space-3) 0 var(--space-1); - font: var(--text-subheadline); - color: var(--text-secondary); - } - .modal input[type="text"] { - width: 100%; - height: 24px; - padding: 0 var(--space-2); - background: var(--bg-input); - border: 0.5px solid var(--border); - border-radius: var(--radius-sm); - color: var(--text-primary); - font: var(--text-body); - box-sizing: border-box; - outline: none; - } - .modal input[type="text"]:focus { - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--blue-tint); - } - .hint { - font: var(--text-footnote); - color: var(--text-secondary); - margin: 0 0 var(--space-3); - } - .btn-file-select { - width: 100%; - padding: var(--space-3); - background: var(--bg-card); - border: 1px dashed var(--border); - border-radius: var(--radius-sm); - color: var(--text-primary); - font: var(--text-body); - cursor: pointer; - margin-bottom: var(--space-2); - } - @media (prefers-reduced-motion: no-preference) { - .btn-file-select { - transition: background-color var(--dur-fast) var(--ease-out), - border-color var(--dur-fast) var(--ease-out); - } - } - .btn-file-select:hover { - background: var(--bg-hover); - border-color: var(--accent); - } - .preview { - margin: var(--space-3) 0; - padding: var(--space-3); - background: var(--editor-bg); - border: 0.5px solid var(--editor-border); - border-radius: var(--radius-sm); - font: 10px/14px var(--font-mono); - color: var(--text-secondary); - max-height: 200px; - overflow-y: auto; - white-space: pre-wrap; - } - .errors { - margin: var(--space-2) 0; - padding: var(--space-2) var(--space-3); - background: var(--error-bg); - border: 0.5px solid var(--red); - border-radius: var(--radius-sm); - } - .errors p { - margin: var(--space-1) 0; - color: var(--error-text); - font: var(--text-body); - } - .modal-footer { - display: flex; - gap: var(--space-2); - justify-content: flex-end; - margin-top: var(--space-4); - } - .btn { - height: 28px; - padding: 0 var(--space-3); - border: 0; - border-radius: var(--radius-sm); - font: var(--text-headline); - cursor: pointer; - color: var(--text-primary); - display: inline-flex; - align-items: center; - justify-content: center; - } - .btn:disabled { opacity: 0.45; cursor: not-allowed; } - .btn-connect { - background: var(--accent); - color: var(--text-inverse); - } - @media (prefers-reduced-motion: no-preference) { - .btn, .btn-connect { - transition: background-color var(--dur-fast) var(--ease-out), - filter var(--dur-fast) var(--ease-out); - } - } - .btn-connect:hover:not(:disabled) { filter: brightness(1.08); } - .btn-connect:active:not(:disabled) { filter: brightness(0.94); } diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 8de23e7..b477aeb 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -1,11 +1,9 @@ { "app": { - "name": "WireGuide", "disconnected": "Disconnected", "connecting": "Connecting...", "connected": "Connected", - "no_handshake": "No Handshake", - "error": "Error" + "no_handshake": "No Handshake" }, "nav": { "tunnels": "Tunnels", @@ -63,21 +61,6 @@ "rx": "Download", "tx": "Upload", "latency": "Latency", - "wifi_auto_connect": "Auto-Connect", - "wifi_auto_connect_hint": "This tunnel activates when you join one of these networks.", - "wifi_empty": "No networks added yet.", - "wifi_added": "Auto-connect networks", - "wifi_known": "Saved networks", - "wifi_current": "Current", - "wifi_current_tooltip": "You are connected to this network now", - "wifi_manual": "Add a network not listed above", - "wifi_manual_hint": "Type an SSID exactly as it appears on the network you want to match.", - "wifi_no_known": "No saved Wi-Fi networks were found. Use the manual input below.", - "wifi_pick_label": "Networks that should auto-connect this tunnel", - "wifi_combo_placeholder": "Type or pick a network…", - "kill_switch": "Kill Switch", - "dns_protection": "DNS Protection", - "details": "Details", "rename_hint": "Double-click to rename", "notes": "Notes", "notes_placeholder": "Freeform notes — endpoint location, credentials hint, etc.", @@ -86,21 +69,8 @@ "sort_added_old": "Oldest first" }, "import": { - "title": "Import Tunnel", - "drop_file": "Drop .conf file here", - "or": "or", - "select_file": "Select File", - "from_clipboard": "Paste from Clipboard", - "preview_title": "Preview", - "tunnel_name": "Tunnel Name", - "confirm": "Import", - "cancel": "Cancel", - "success": "Tunnel imported successfully", - "exists": "Tunnel \"{name}\" already exists. Overwrite?", - "overwrite": "Overwrite", "zip_result_title": "Import Results", "zip_ok": "Done", - "zip_none": "No .conf files found in zip", "zip_summary": "{ok} imported, {fail} failed", "zip_summary_ok": "{ok} imported" }, @@ -108,17 +78,9 @@ "title": "Edit Tunnel: {name}", "save": "Save", "cancel": "Cancel", - "validation_error": "Validation Error", "name_placeholder": "Tunnel name", "name_required": "Tunnel name is required" }, - "scripts": { - "warning_title": "Script Warning", - "warning_message": "This configuration contains system commands that will be executed:", - "allow": "Allow", - "deny": "Deny", - "denied_note": "Scripts will not be executed for this tunnel." - }, "settings": { "title": "Settings", "general": "General", @@ -133,7 +95,6 @@ "lang_auto": "Auto", "close": "Close", "advanced": "Advanced", - "wifi_rules": "Wi-Fi Rules", "log_level": "Log Level", "log_level_debug": "Debug", "log_level_info": "Info", @@ -150,13 +111,10 @@ "about": "About", "about_tagline": "Friendly WireGuard for every desktop.", "about_desc": "An open-source VPN client built around WireGuard — drop a config and you're online. A kill switch that doesn't leak, DNS protection that doesn't dodge, and Wi-Fi rules that pick the right tunnel on networks you trust. No command line. No telemetry. No upsell.", - "about_source": "Source on GitHub", "about_releases": "Release notes", "about_issues": "Report an issue", "about_license": "License (MIT)", "about_credits": "Built with WireGuard®, Wails, and Svelte.", - "about_copyright": "© 2026 WireGuide. Released under the MIT License.", - "about_check_update": "Check for updates", "about_made_by": "Made by", "about_footer_copyright": "© 2026 WireGuide", "up_to_date": "You're on the latest version.", @@ -174,41 +132,15 @@ "delete_message": "Are you sure you want to delete \"{name}\"?", "yes": "Delete", "no": "Cancel", - "close": "Close", "disconnect_first": "Disconnect the tunnel first" }, - "error": { - "connection_failed": "Connection failed", - "network_error": "Network error — check your internet connection", - "key_mismatch": "Key mismatch — verify your configuration", - "permission_denied": "Administrator privileges required", - "invalid_config": "Invalid configuration", - "file_read_error": "Cannot read file — check file permissions", - "empty_file": "Empty file" - }, - "time": { - "seconds_ago": "{n}s ago", - "just_now": "just now" - }, "log": { - "title": "Logs", "clear": "Clear", "copy": "Copy", "auto_scroll": "Auto-scroll", "no_entries": "No log entries", "filter_all": "All" }, - "notify": { - "disconnected": "VPN connection lost", - "reconnecting": "Reconnecting to {name}...", - "reconnected": "Reconnected to {name}", - "kill_switch_active": "Kill switch active — internet blocked", - "error": "VPN error: {message}" - }, - "daemon": { - "not_running": "WireGuide daemon is not running", - "install_hint": "Start the daemon with: sudo wireguided" - }, "tools": { "tab_dns_leak": "DNS Leak Test", "tab_routes": "Routes", @@ -229,44 +161,9 @@ "route_legend_vpn": "VPN route", "route_legend_direct": "Direct route" }, - "new_tunnel": { - "title": "New Tunnel", - "mode_form": "Form", - "mode_text": "Text", - "section_interface": "Interface", - "name_label": "Name", - "name_placeholder": "my-tunnel", - "private_key_label": "Private Key", - "generate": "Generate", - "address_label": "Address", - "dns_label": "DNS", - "mtu_label": "MTU", - "section_peer": "Peer", - "peer_public_key_label": "Public Key", - "peer_psk_label": "Preshared Key (optional)", - "peer_endpoint_label": "Endpoint", - "peer_allowed_ips_label": "Allowed IPs", - "peer_keepalive_label": "Persistent Keepalive", - "create": "Create", - "cancel": "Cancel" - }, - "keygen": { - "private_key": "Private Key", - "public_key": "Public Key", - "generate": "Generate New Keypair", - "copy": "Copy" - }, - "split_tunnel": { - "title": "Split Tunnel", - "no_subnets": "No subnets added", - "add": "Add", - "subnet_placeholder": "10.0.0.0/24" - }, "stats": { "speed_graph": "Speed Graph", - "waiting": "Waiting for data...", - "rx": "Download", - "tx": "Upload" + "waiting": "Waiting for data..." }, "conflict": { "title": "Routing Conflict Detected", @@ -287,11 +184,8 @@ "vpn_warning": "The VPN connection will be briefly interrupted during the update. The app will restart automatically.", "proceed": "Update Now", "cancel": "Later", - "copy_command": "Copy", - "copied": "Copied", "check_now": "Check now", "checking": "Checking…", - "up_to_date": "You're on the latest version ({version})", "last_checked": "Last checked {time}", "never_checked": "Never checked", "first_check_scheduled": "First check scheduled…", @@ -308,13 +202,6 @@ "desc": "Wi-Fi auto-connect rules need access to your current network name. Grant permission in System Settings → Privacy & Security → Location Services.", "open_settings": "Open Settings" }, - "wifi_rules": { - "trusted_ssids": "Trusted Networks", - "trusted_hint": "On these networks, all auto-managed tunnels disconnect automatically.", - "no_trusted": "No trusted networks added.", - "add": "Add", - "ssid_placeholder": "Network name (SSID)" - }, "automation": { "title": "Automation", "hint": "Rules run top to bottom; the first matching condition wins. Pick a condition and type its value, or use the suggestion for the network you're on. \"This network\" matches a specific router by its gateway MAC (precise; tells apart networks that share a subnet); \"subnet\" matches an IP range. Add separate rules to connect and to disconnect.", @@ -330,15 +217,10 @@ "cond_none": "otherwise", "cond_none_desc": "no other rule matched", "ssid_placeholder": "Wi-Fi name", - "subnet_current": "current network", "cond_network": "on this network", - "use_current": "Use current", - "net_captured": "this network", - "net_not_set": "click \"Use current\" while on the network", - "net_placeholder": "pick a network or click \"Use current\"", "mac_placeholder": "gateway MAC (e.g. b0:38:6c:54:8b:ab)", "drag_hint": "drag to reorder — the topmost matching rule wins", "mac_invalid": "not a valid MAC address (e.g. b0:38:6c:54:8b:ab)", "subnet_invalid": "not a valid CIDR (e.g. 192.168.0.0/24)" } -} \ No newline at end of file +} diff --git a/frontend/src/i18n/index.js b/frontend/src/i18n/index.js index b886e96..d27ecb9 100644 --- a/frontend/src/i18n/index.js +++ b/frontend/src/i18n/index.js @@ -48,13 +48,5 @@ function translate(lang, key, params = {}) { // `svelte-i18n`), conceptually equivalent to Swift's `L.tr("key")`. export const t = derived(locale, ($locale) => (key, params) => translate($locale, key, params)); -// Non-reactive translator for plain-JS call sites (notifications, log -// formatting, etc.) that aren't tied to the Svelte render tree. Reads -// the current locale once per call. Do NOT use this inside `.svelte` -// templates — use `$t(...)` there so re-renders happen on language change. -export function tPlain(key, params = {}) { - return translate(get(locale), key, params); -} - // Initialize with detected language locale.set(detectLanguage()); diff --git a/frontend/src/i18n/ja.json b/frontend/src/i18n/ja.json index e232cc4..a3d5ca0 100644 --- a/frontend/src/i18n/ja.json +++ b/frontend/src/i18n/ja.json @@ -1,11 +1,9 @@ { "app": { - "name": "WireGuide", "disconnected": "未接続", "connecting": "接続中…", "connected": "接続済み", - "no_handshake": "ハンドシェイクなし", - "error": "エラー" + "no_handshake": "ハンドシェイクなし" }, "nav": { "tunnels": "トンネル", @@ -63,21 +61,6 @@ "rx": "ダウンロード", "tx": "アップロード", "latency": "遅延", - "wifi_auto_connect": "自動接続", - "wifi_auto_connect_hint": "これらのネットワークに接続するとこのトンネルが自動で起動します。", - "wifi_empty": "登録されたネットワークがありません。", - "wifi_added": "自動接続ネットワーク", - "wifi_known": "保存済みネットワーク", - "wifi_current": "現在", - "wifi_current_tooltip": "現在このネットワークに接続中です", - "wifi_manual": "リストにないネットワークを追加", - "wifi_manual_hint": "対象ネットワークの SSID を正確に入力してください。", - "wifi_no_known": "保存済み Wi-Fi ネットワークが見つかりませんでした。下から手動で入力してください。", - "wifi_pick_label": "このトンネルを自動で起動するネットワーク", - "wifi_combo_placeholder": "ネットワークを入力または選択…", - "kill_switch": "キルスイッチ", - "dns_protection": "DNS 保護", - "details": "詳細", "rename_hint": "ダブルクリックで名前変更", "notes": "メモ", "notes_placeholder": "エンドポイントの場所、認証情報のヒントなど自由メモ", @@ -86,21 +69,8 @@ "sort_added_old": "古い順" }, "import": { - "title": "トンネルのインポート", - "drop_file": ".conf ファイルをここにドロップ", - "or": "または", - "select_file": "ファイルを選択", - "from_clipboard": "クリップボードから貼り付け", - "preview_title": "プレビュー", - "tunnel_name": "トンネル名", - "confirm": "インポート", - "cancel": "キャンセル", - "success": "トンネルをインポートしました", - "exists": "トンネル \"{name}\" はすでに存在します。上書きしますか?", - "overwrite": "上書き", "zip_result_title": "インポート結果", "zip_ok": "完了", - "zip_none": "zip 内に .conf ファイルが見つかりませんでした", "zip_summary": "{ok}件インポート、{fail}件失敗", "zip_summary_ok": "{ok}件インポート" }, @@ -108,17 +78,9 @@ "title": "トンネル編集: {name}", "save": "保存", "cancel": "キャンセル", - "validation_error": "検証エラー", "name_placeholder": "トンネル名", "name_required": "トンネル名を入力してください" }, - "scripts": { - "warning_title": "スクリプト警告", - "warning_message": "この設定には実行されるシステムコマンドが含まれています:", - "allow": "許可", - "deny": "拒否", - "denied_note": "このトンネルではスクリプトは実行されません。" - }, "settings": { "title": "設定", "general": "一般", @@ -133,7 +95,6 @@ "lang_auto": "自動", "close": "閉じる", "advanced": "詳細", - "wifi_rules": "Wi-Fi ルール", "log_level": "ログレベル", "log_level_debug": "デバッグ", "log_level_info": "情報", @@ -150,13 +111,10 @@ "about": "情報", "about_tagline": "デスクトップのための、親しみやすい WireGuard。", "about_desc": "WireGuard を中心に作られたオープンソースの VPN クライアント。設定ファイルをドロップすればすぐにつながります。漏えいしないキルスイッチ、抜け道のない DNS 保護、信頼するネットワークで自動的にトンネルを開く Wi-Fi ルール。コマンドラインも、テレメトリも、勧誘もありません。", - "about_source": "GitHub ソースコード", "about_releases": "リリースノート", "about_issues": "問題を報告", "about_license": "ライセンス (MIT)", "about_credits": "WireGuide®、Wails、Svelte で構築。", - "about_copyright": "© 2026 WireGuide. MIT ライセンスで公開。", - "about_check_update": "アップデートを確認", "about_made_by": "作者", "about_footer_copyright": "© 2026 WireGuide", "section_appearance": "外観", @@ -174,41 +132,15 @@ "delete_message": "\"{name}\" を削除しますか?", "yes": "削除", "no": "キャンセル", - "close": "閉じる", "disconnect_first": "先にトンネルを切断してください" }, - "error": { - "connection_failed": "接続に失敗しました", - "network_error": "ネットワークエラー — インターネット接続を確認してください", - "key_mismatch": "鍵の不一致 — 設定を確認してください", - "permission_denied": "管理者権限が必要です", - "invalid_config": "無効な設定", - "file_read_error": "ファイルを読み込めません — ファイル権限を確認してください", - "empty_file": "空のファイルです" - }, - "time": { - "seconds_ago": "{n}秒前", - "just_now": "今" - }, "log": { - "title": "ログ", "clear": "クリア", "auto_scroll": "自動スクロール", "no_entries": "ログがありません", "filter_all": "すべて", "copy": "コピー" }, - "notify": { - "disconnected": "VPN 接続が切断されました", - "reconnecting": "{name} に再接続中…", - "reconnected": "{name} に再接続しました", - "kill_switch_active": "キルスイッチ有効 — インターネットをブロック中", - "error": "VPN エラー: {message}" - }, - "daemon": { - "not_running": "WireGuide デーモンが実行されていません", - "install_hint": "デーモン起動: sudo wireguided" - }, "tools": { "tab_dns_leak": "DNS 漏洩テスト", "tab_routes": "ルート", @@ -229,44 +161,9 @@ "route_legend_vpn": "VPN ルート", "route_legend_direct": "直接ルート" }, - "new_tunnel": { - "title": "新しいトンネル", - "mode_form": "フォーム", - "mode_text": "テキスト", - "section_interface": "インターフェース", - "name_label": "名前", - "name_placeholder": "my-tunnel", - "private_key_label": "秘密鍵", - "generate": "生成", - "address_label": "アドレス", - "dns_label": "DNS", - "mtu_label": "MTU", - "section_peer": "ピア", - "peer_public_key_label": "公開鍵", - "peer_psk_label": "事前共有鍵 (任意)", - "peer_endpoint_label": "エンドポイント", - "peer_allowed_ips_label": "許可 IP", - "peer_keepalive_label": "Persistent Keepalive", - "create": "作成", - "cancel": "キャンセル" - }, - "keygen": { - "private_key": "秘密鍵", - "public_key": "公開鍵", - "generate": "新しい鍵ペアを生成", - "copy": "コピー" - }, - "split_tunnel": { - "title": "スプリットトンネル", - "no_subnets": "サブネットが追加されていません", - "add": "追加", - "subnet_placeholder": "10.0.0.0/24" - }, "stats": { "speed_graph": "速度グラフ", - "waiting": "データ待機中…", - "rx": "ダウンロード", - "tx": "アップロード" + "waiting": "データ待機中…" }, "conflict": { "title": "ルーティング競合を検出", @@ -287,11 +184,8 @@ "vpn_warning": "更新中に VPN 接続が一時的に切断される場合があります。アプリは自動的に再起動します。", "proceed": "更新する", "cancel": "あとで", - "copy_command": "コピー", - "copied": "コピーしました", "check_now": "今すぐ確認", "checking": "確認中…", - "up_to_date": "最新バージョンです ({version})", "last_checked": "前回 {time}", "never_checked": "未確認", "first_check_scheduled": "まもなく初回確認…", @@ -308,13 +202,6 @@ "desc": "Wi-Fi 自動接続ルールが動作するには、現在のネットワーク名へのアクセスが必要です。System Settings → プライバシーとセキュリティ → 位置情報サービスで許可してください。", "open_settings": "設定を開く" }, - "wifi_rules": { - "trusted_ssids": "信頼するネットワーク", - "trusted_hint": "これらのネットワークに接続すると、自動接続中のトンネルがすべて自動的に切断されます。", - "no_trusted": "信頼するネットワークが追加されていません。", - "add": "追加", - "ssid_placeholder": "ネットワーク名 (SSID)" - }, "automation": { "title": "オートメーション", "hint": "ルールは上から順に評価され、最初に一致した条件が適用されます。条件を選んで値を入力するか、現在のネットワークの候補を使用してください。「このネットワーク」はゲートウェイMACで特定のルーターを正確に識別し(サブネットが重複しても区別)、「サブネット」はIP範囲で一致します。接続/切断のルールを個別に追加できます。", @@ -330,15 +217,10 @@ "cond_none": "それ以外", "cond_none_desc": "他のルールに一致しないとき", "ssid_placeholder": "Wi-Fi名", - "subnet_current": "現在のネットワーク", "cond_network": "このネットワークで", - "use_current": "現在を使用", - "net_captured": "このネットワーク", - "net_not_set": "対象ネットワークで「現在を使用」をクリック", - "net_placeholder": "ネットワークを選択、または「現在を使用」", "mac_placeholder": "ゲートウェイMAC (例: b0:38:6c:54:8b:ab)", "drag_hint": "ドラッグで並べ替え — 最上位の一致ルールが優先", "mac_invalid": "有効なMACアドレスではありません (例: b0:38:6c:54:8b:ab)", "subnet_invalid": "有効なCIDRではありません (例: 192.168.0.0/24)" } -} \ No newline at end of file +} diff --git a/frontend/src/i18n/ko.json b/frontend/src/i18n/ko.json index 33156a1..5bd1cbc 100644 --- a/frontend/src/i18n/ko.json +++ b/frontend/src/i18n/ko.json @@ -1,11 +1,9 @@ { "app": { - "name": "WireGuide", "disconnected": "연결 안 됨", "connecting": "연결 중…", "connected": "연결됨", - "no_handshake": "핸드셰이크 없음", - "error": "오류" + "no_handshake": "핸드셰이크 없음" }, "nav": { "tunnels": "터널", @@ -63,21 +61,6 @@ "rx": "다운로드", "tx": "업로드", "latency": "지연 시간", - "wifi_auto_connect": "자동 연결", - "wifi_auto_connect_hint": "이 네트워크에 접속하면 이 터널이 자동으로 켜집니다.", - "wifi_empty": "추가된 네트워크가 없습니다.", - "wifi_added": "자동 연결 네트워크", - "wifi_known": "저장된 네트워크", - "wifi_current": "현재", - "wifi_current_tooltip": "지금 이 네트워크에 연결되어 있습니다", - "wifi_manual": "목록에 없는 네트워크 추가", - "wifi_manual_hint": "원하는 네트워크의 SSID를 정확히 입력하세요.", - "wifi_no_known": "저장된 Wi-Fi 네트워크를 찾지 못했습니다. 아래에서 직접 입력하세요.", - "wifi_pick_label": "이 터널을 자동으로 켤 네트워크", - "wifi_combo_placeholder": "네트워크 입력 또는 선택…", - "kill_switch": "킬 스위치", - "dns_protection": "DNS 보호", - "details": "상세 정보", "rename_hint": "두 번 클릭해서 이름 변경", "notes": "메모", "notes_placeholder": "엔드포인트 위치, 자격증명 힌트 등 자유 메모", @@ -86,21 +69,8 @@ "sort_added_old": "오래된 추가순" }, "import": { - "title": "터널 가져오기", - "drop_file": ".conf 파일을 여기에 놓으세요", - "or": "또는", - "select_file": "파일 선택", - "from_clipboard": "클립보드에서 붙여넣기", - "preview_title": "미리보기", - "tunnel_name": "터널 이름", - "confirm": "가져오기", - "cancel": "취소", - "success": "터널을 가져왔습니다", - "exists": "터널 \"{name}\"이(가) 이미 있습니다. 덮어쓰시겠습니까?", - "overwrite": "덮어쓰기", "zip_result_title": "가져오기 결과", "zip_ok": "완료", - "zip_none": "zip에서 .conf 파일을 찾을 수 없습니다", "zip_summary": "{ok}개 가져옴, {fail}개 실패", "zip_summary_ok": "{ok}개 가져옴" }, @@ -108,17 +78,9 @@ "title": "터널 편집: {name}", "save": "저장", "cancel": "취소", - "validation_error": "유효성 검사 오류", "name_placeholder": "터널 이름", "name_required": "터널 이름을 입력하세요" }, - "scripts": { - "warning_title": "스크립트 경고", - "warning_message": "이 설정에는 실행될 시스템 명령이 포함되어 있습니다:", - "allow": "허용", - "deny": "거부", - "denied_note": "이 터널의 스크립트는 실행되지 않습니다." - }, "settings": { "title": "설정", "general": "일반", @@ -133,7 +95,6 @@ "lang_auto": "자동", "close": "닫기", "advanced": "고급", - "wifi_rules": "Wi-Fi 규칙", "log_level": "로그 수준", "log_level_debug": "디버그", "log_level_info": "정보", @@ -150,13 +111,10 @@ "about": "정보", "about_tagline": "데스크톱을 위한 친근한 WireGuard.", "about_desc": "WireGuard를 중심으로 만든 오픈소스 VPN 클라이언트. 설정 파일을 끌어다 놓으면 바로 연결됩니다. 누수 없는 킬 스위치, 빈틈 없는 DNS 보호, 신뢰하는 네트워크에서 자동으로 터널을 켜는 Wi-Fi 규칙까지. 명령줄도, 텔레메트리도, 영업도 없습니다.", - "about_source": "GitHub 소스 코드", "about_releases": "릴리스 노트", "about_issues": "문제 신고", "about_license": "라이선스 (MIT)", "about_credits": "WireGuide®, Wails, Svelte 기반.", - "about_copyright": "© 2026 WireGuide. MIT 라이선스로 공개.", - "about_check_update": "업데이트 확인", "about_made_by": "만든 사람", "about_footer_copyright": "© 2026 WireGuide", "section_appearance": "외관", @@ -174,41 +132,15 @@ "delete_message": "\"{name}\"을(를) 삭제하시겠습니까?", "yes": "삭제", "no": "취소", - "close": "닫기", "disconnect_first": "먼저 연결을 해제하세요" }, - "error": { - "connection_failed": "연결 실패", - "network_error": "네트워크 오류 — 인터넷 연결을 확인하세요", - "key_mismatch": "키 불일치 — 설정을 확인하세요", - "permission_denied": "관리자 권한이 필요합니다", - "invalid_config": "유효하지 않은 설정", - "file_read_error": "파일을 읽을 수 없습니다 — 파일 권한을 확인하세요", - "empty_file": "빈 파일입니다" - }, - "time": { - "seconds_ago": "{n}초 전", - "just_now": "방금" - }, "log": { - "title": "로그", "clear": "지우기", "auto_scroll": "자동 스크롤", "no_entries": "로그 항목 없음", "filter_all": "전체", "copy": "복사" }, - "notify": { - "disconnected": "VPN 연결이 끊겼습니다", - "reconnecting": "{name}에 재연결 중…", - "reconnected": "{name}에 재연결되었습니다", - "kill_switch_active": "킬 스위치 활성화 — 인터넷 차단됨", - "error": "VPN 오류: {message}" - }, - "daemon": { - "not_running": "WireGuide 데몬이 실행되지 않고 있습니다", - "install_hint": "데몬 시작: sudo wireguided" - }, "tools": { "tab_dns_leak": "DNS 유출 검사", "tab_routes": "라우트", @@ -229,44 +161,9 @@ "route_legend_vpn": "VPN 경로", "route_legend_direct": "직접 경로" }, - "new_tunnel": { - "title": "새 터널", - "mode_form": "폼", - "mode_text": "텍스트", - "section_interface": "인터페이스", - "name_label": "이름", - "name_placeholder": "my-tunnel", - "private_key_label": "개인 키", - "generate": "생성", - "address_label": "주소", - "dns_label": "DNS", - "mtu_label": "MTU", - "section_peer": "피어", - "peer_public_key_label": "공개 키", - "peer_psk_label": "사전 공유 키 (선택)", - "peer_endpoint_label": "엔드포인트", - "peer_allowed_ips_label": "허용 IP", - "peer_keepalive_label": "Persistent Keepalive", - "create": "만들기", - "cancel": "취소" - }, - "keygen": { - "private_key": "개인 키", - "public_key": "공개 키", - "generate": "키 쌍 새로 생성", - "copy": "복사" - }, - "split_tunnel": { - "title": "스플릿 터널", - "no_subnets": "추가된 서브넷이 없습니다", - "add": "추가", - "subnet_placeholder": "10.0.0.0/24" - }, "stats": { "speed_graph": "속도 그래프", - "waiting": "데이터 대기 중…", - "rx": "다운로드", - "tx": "업로드" + "waiting": "데이터 대기 중…" }, "conflict": { "title": "라우팅 충돌 감지", @@ -287,11 +184,8 @@ "vpn_warning": "업데이트 중 VPN 연결이 잠시 끊길 수 있습니다. 앱이 자동으로 재시작됩니다.", "proceed": "업데이트", "cancel": "나중에", - "copy_command": "복사", - "copied": "복사됨", "check_now": "지금 확인", "checking": "확인 중…", - "up_to_date": "최신 버전입니다 ({version})", "last_checked": "마지막 확인 {time}", "never_checked": "확인 기록 없음", "first_check_scheduled": "곧 첫 확인 예정…", @@ -308,13 +202,6 @@ "desc": "Wi-Fi 자동 연결 규칙이 동작하려면 현재 Wi-Fi 이름에 접근할 수 있어야 합니다. System Settings → 개인 정보 보호 및 보안 → 위치 서비스에서 허용해주세요.", "open_settings": "설정 열기" }, - "wifi_rules": { - "trusted_ssids": "신뢰하는 네트워크", - "trusted_hint": "이 네트워크에 접속하면 자동 연결된 모든 터널이 자동으로 해제됩니다.", - "no_trusted": "추가된 신뢰 네트워크가 없습니다.", - "add": "추가", - "ssid_placeholder": "네트워크 이름 (SSID)" - }, "automation": { "title": "자동화", "hint": "규칙은 위에서 아래로 평가되며 먼저 맞는 조건이 적용됩니다. 조건을 고르고 값을 직접 입력하거나, 현재 네트워크의 자동완성을 쓰세요. \"이 네트워크\"는 게이트웨이 MAC으로 특정 공유기를 정확히 식별하고(서브넷이 겹쳐도 구분), \"서브넷\"은 IP 대역으로 매칭합니다. 연결/해제 규칙을 각각 추가하세요.", @@ -330,15 +217,10 @@ "cond_none": "그 외", "cond_none_desc": "다른 규칙이 안 맞을 때", "ssid_placeholder": "Wi-Fi 이름", - "subnet_current": "현재 네트워크", "cond_network": "이 네트워크에서", - "use_current": "현재 사용", - "net_captured": "이 네트워크", - "net_not_set": "해당 네트워크에서 \"현재 사용\" 클릭", - "net_placeholder": "네트워크 선택 또는 \"현재 사용\" 클릭", "mac_placeholder": "게이트웨이 MAC (예: b0:38:6c:54:8b:ab)", "drag_hint": "드래그해서 순서 변경 — 맨 위의 일치 규칙이 우선", "mac_invalid": "올바른 MAC 주소가 아닙니다 (예: b0:38:6c:54:8b:ab)", "subnet_invalid": "올바른 CIDR이 아닙니다 (예: 192.168.0.0/24)" } -} \ No newline at end of file +} diff --git a/frontend/src/lib/AutomationEditor.svelte b/frontend/src/lib/AutomationEditor.svelte index d6680f4..18a2d9b 100644 --- a/frontend/src/lib/AutomationEditor.svelte +++ b/frontend/src/lib/AutomationEditor.svelte @@ -537,15 +537,6 @@ border-color: var(--error-text, #ff453a); background: color-mix(in srgb, var(--error-text, #ff453a) 8%, var(--bg-primary)); } - .am-val-network { flex: 1; min-width: 120px; display: inline-flex; align-items: baseline; gap: 6px; font: 400 12px var(--font-sans); color: var(--text-primary); } - .am-mac { font: 400 10px var(--font-mono); color: var(--text-muted); } - .am-usecurrent { - font: 500 11px var(--font-sans); color: var(--accent); - background: color-mix(in srgb, var(--accent) 10%, transparent); - border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); - border-radius: 7px; padding: 4px 9px; cursor: pointer; flex-shrink: 0; white-space: nowrap; - } - .am-usecurrent:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); } .am-remove { background: transparent; border: 0; color: var(--text-muted); cursor: pointer; padding: 4px; border-radius: 6px; flex-shrink: 0; } .am-remove:hover { background: color-mix(in srgb, var(--red, #ff3b30) 18%, transparent); color: var(--red, #ff3b30); } .am-add { diff --git a/frontend/src/lib/KeyGenerator.svelte b/frontend/src/lib/KeyGenerator.svelte deleted file mode 100644 index 1c61dc8..0000000 --- a/frontend/src/lib/KeyGenerator.svelte +++ /dev/null @@ -1,102 +0,0 @@ - - -
- - - {#if generated} -
-
- - {privateKey.substring(0, 20)}… -
-
- - - {publicKey} - - {copied ? '✓' : $t('keygen.copy')} -
-
- {/if} -
- - diff --git a/frontend/src/lib/LogViewer.svelte b/frontend/src/lib/LogViewer.svelte index 4d6ac14..b932d88 100644 --- a/frontend/src/lib/LogViewer.svelte +++ b/frontend/src/lib/LogViewer.svelte @@ -120,7 +120,7 @@
- {#each filtered as entry, i (i)} + {#each filtered as entry (entry.id)}
{formatTime(entry.time)} {entry.source} diff --git a/frontend/src/lib/MiniMode.svelte b/frontend/src/lib/MiniMode.svelte deleted file mode 100644 index 2165f21..0000000 --- a/frontend/src/lib/MiniMode.svelte +++ /dev/null @@ -1,115 +0,0 @@ - - -
-
- - {status?.tunnel_name || 'WireGuide'} - -
- - {#if isConnected} -
- ↓ {formatBytes(status.rx_bytes)} - ↑ {formatBytes(status.tx_bytes)} -
- {/if} - - -
- - diff --git a/frontend/src/lib/ScriptWarning.svelte b/frontend/src/lib/ScriptWarning.svelte deleted file mode 100644 index c69d3e2..0000000 --- a/frontend/src/lib/ScriptWarning.svelte +++ /dev/null @@ -1,123 +0,0 @@ - - - - - diff --git a/frontend/src/lib/Settings.svelte b/frontend/src/lib/Settings.svelte index a948e35..de6fa7c 100644 --- a/frontend/src/lib/Settings.svelte +++ b/frontend/src/lib/Settings.svelte @@ -136,10 +136,6 @@ tray_icon_style: 'color', auto_update_check: true, compact_list: false, - wifi_rules: { - trusted_ssids: [], - per_tunnel: {}, - }, }; let loaded = false; let appVersion = ''; @@ -162,12 +158,6 @@ // the Go side becomes undefined here; default to true to match // Settings.AutoUpdateCheckEnabled() semantics. settings.auto_update_check = (s.auto_update_check === false) ? false : true; - if (s.wifi_rules) { - settings.wifi_rules = { - trusted_ssids: s.wifi_rules.trusted_ssids || [], - per_tunnel: s.wifi_rules.per_tunnel || {}, - }; - } } } catch (e) { console.error('load settings:', e); @@ -180,8 +170,8 @@ // Re-fetch the freshest settings.json before writing so per-tunnel // wifi rule edits made in TunnelDetail (which calls SaveSettings // independently with its own modified per_tunnel map) aren't - // silently overwritten. We own only `trusted_ssids` in wifi_rules; - // `per_tunnel` belongs to TunnelDetail. If the fresh fetch fails + // silently overwritten. This screen owns NO part of wifi_rules — + // both halves are carried from the fresh fetch. If the fresh fetch fails // (helper restarting, IPC flake) we abort rather than write our // potentially-stale per_tunnel snapshot — a deferred save is far // better than clobbering the user's per-tunnel edits. @@ -216,8 +206,13 @@ // them from the fresh fetch so saving a Settings toggle never // wipes them. automation: fresh?.automation, + // Legacy Wi-Fi trust rules have no UI on this screen — carry both + // halves from the fresh fetch (not a load-time snapshot) so a + // Settings toggle can't clobber CLI edits made while it is open. + // The Go side still migrates trusted_ssids/per_tunnel into the + // Automation model, so the round-trip itself must stay. wifi_rules: { - trusted_ssids: settings.wifi_rules?.trusted_ssids || [], + trusted_ssids: fresh?.wifi_rules?.trusted_ssids || [], per_tunnel: perTunnel, }, }); diff --git a/frontend/src/lib/SplitTunnelUI.svelte b/frontend/src/lib/SplitTunnelUI.svelte deleted file mode 100644 index 7177952..0000000 --- a/frontend/src/lib/SplitTunnelUI.svelte +++ /dev/null @@ -1,149 +0,0 @@ - - -
-
- - -
- - {#if mode === 'custom'} -
- {#each customIPs as ip} -
- {ip} - -
- {/each} - {#if customIPs.length === 0} -

{$t('split_tunnel.no_subnets')}

- {/if} -
-
- - -
- {/if} -
- - diff --git a/frontend/src/lib/TunnelList.svelte b/frontend/src/lib/TunnelList.svelte index 251196a..1c8a651 100644 --- a/frontend/src/lib/TunnelList.svelte +++ b/frontend/src/lib/TunnelList.svelte @@ -115,7 +115,7 @@

{$t('tunnel.drop_hint')}

{:else} - {#each sorted as tun} + {#each sorted as tun (tun.name)}