Skip to content

Fix zoom controls contrast in topology view - #1066

Merged
decolua merged 1 commit into
decolua:masterfrom
IzayoiS:fix/usage-stats-zoom-contrast
May 13, 2026
Merged

decolua merged 1 commit into
decolua:masterfrom
IzayoiS:fix/usage-stats-zoom-contrast

Conversation

@IzayoiS

@IzayoiS IzayoiS commented May 13, 2026

Copy link
Copy Markdown
Contributor

Description

Fix zoom controls contrast in provider topology view. Controls now match app theme (light/dark mode) instead of default white background.

Changes

  • Add custom class to ReactFlow Controls component
  • Add theme-aware CSS styling for zoom buttons (background, border, text colors)

Before/After

  • Before: White zoom controls with poor contrast
  • After: Controls use app surface/border/text colors, readable in both themes
image

@decolua
decolua merged commit 003be82 into decolua:master May 13, 2026
decolua pushed a commit that referenced this pull request May 13, 2026
## Features
- Add MiniMax TTS provider support (#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (#1064, #1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (#1080)
- Fix zoom controls contrast in topology view (#1066)
caothu159 added a commit to diepxuan/9router that referenced this pull request May 14, 2026
* fix: improve dropdown text readability in dark theme on usage page (decolua#997)

- Add global CSS rules for select elements in dark mode
- Use color-scheme property to signal dark mode to browser
- Explicitly style option elements with dark theme colors
- Fix UsageStats dropdown to use correct CSS variables (bg-surface, text-text-main)

Fixes dropdown text being unreadable in dark theme on usage page:
- Provider filter dropdown
- Table view selector (Model/Account/API Key/Endpoint)
- Pagination page size selector

Tested in Chrome and Firefox with both light and dark themes.

* fix(security): scope OAuth callback postMessage targets and re-enable TLS verification on DNS-bypass fetch (decolua#998)

Two findings, neither blocked by anything else:

1. src/app/callback/page.js — the OAuth callback page posted the
   { code, state } payload to window.opener with targetOrigin "*", so any
   page that opened the popup against the well-known redirect_uri received
   the live OAuth code. The expectedOrigins list was already computed but
   never used. Iterate over it and pass the origin per send.

2. open-sse/utils/proxyFetch.js — createBypassRequest() set
   rejectUnauthorized: false on the HTTPS request that runs after the
   Google-DNS-resolved real-IP fallback (used for cloudcode-pa.googleapis,
   GitHub Copilot, Cursor, AWS LLM endpoints). Combined with servername:
   parsedUrl.hostname this gave SNI-correct connections that nonetheless
   ignored cert validation, so an on-path attacker could swap in their
   own cert and read the user's API tokens / prompts. Drop the flag.

Detected by Aeon + semgrep (javascript.browser.security.wildcard-postmessage-configuration
+ problem-based-packs.insecure-transport.js-node.bypass-tls-verification).
Severity: HIGH (#1) / MEDIUM (#2).
CWEs: CWE-1385 (#1), CWE-295 (#2).

Co-authored-by: aeonframework <aeon@aeonframework.dev>

* # v0.4.29 (2026-05-10)

## Features
- Add Cline & Kilo Code tool cards
- Tailscale TUN mode for stable Funnel TLS
- Sort APIKEY providers by usage, collapse to top 20

## Improvements
- Local Material Symbols font (no Google Fonts)
- Docker base: Bun → Node 22-alpine
- MITM reads aliases from JSON cache (no native sqlite)
- Stream stall timeout (2 min) in open-sse

## Fixes
- Fal.ai key test: use stable models endpoint

* Feat : Gitbook

* Gitbook

* Update gitbook

* fix: respect PORT env in internal model-test fetch (decolua#1014)

Internal model test routes fetched 127.0.0.1:UPDATER_CONFIG.appPort
(hardcoded 20128). When PORT env is set to a different value, the app
listens on PORT but the internal fetch still targets 20128, causing
"fetch failed" on /api/models/test and /api/providers/[id]/test-models.

Fall back to UPDATER_CONFIG.appPort only when process.env.PORT is unset.

* fix: normalize developer role to system for OpenAI-format providers (decolua#1011)

Deepseek API (and likely other providers) reject messages with
role: 'developer' — only accept system, user, assistant, tool.
filterToOpenAIFormat() normalizes content blocks but never touched
message roles, so developer passed through unmodified and caused
400 errors (issue decolua#773).

Fix: add one-line developer → system mapping in filterToOpenAIFormat()
before role-specific logic. This is the common normalization point
called for all targetFormat=openai providers (Deepseek, Groq, Mistral,
Perplexity, Together, Fireworks, Cerebras, xAI, NVIDIA, etc.)

Closes decolua#773

* feat(mitm): implement dynamic linux cert resolution and NSS db injection (decolua#1010)

- Replaced hardcoded LINUX_CERT_DIR with dynamic filesystem probing to support Debian, Arch, Fedora, and openSUSE system trust stores.
- Added updateNssDatabases helper to seamlessly inject root certificates directly into browser NSS databases (e.g., ~/.pki/nssdb, ~/.mozilla/firefox).
- Supported standard and snap-based Chrome/Chromium and Firefox installations.
- Made browser cert injection resilient, executing under the current user to prevent file ownership issues, and safely falling back if certutil is absent.

* # v0.4.30 (2026-05-11)
## Features
- MCP stdio→SSE bridge: expose local stdio MCP plugins over SSE (api/mcp/[plugin]/sse, /message)
- Dynamic Linux cert resolution + NSS DB injection (Debian/Arch/Fedora/openSUSE, Chrome/Chromium/Firefox incl. snap) (decolua#1010)
- Cowork tool: expanded settings UI & API
- GitBook docs (DocsContent, DocsLayout)
## Fixes
- OAuth callback postMessage scoped to expected origins (CWE-1385) (decolua#998)
- Re-enable TLS verification on DNS-bypass fetch (CWE-295) (decolua#998)
- Normalize `developer` role → `system` for OpenAI-format providers (Deepseek, Groq, …) (decolua#1011, closes decolua#773)
- Respect `PORT` env in internal model-test fetch (decolua#1014)
- Dropdown text readability in dark theme on usage page (decolua#997)
## Improvements
- Refactor Claude CLI spoof headers into shared constant
- Tool deduper utility in open-sse handlers

* feat(ui): add Done button to ModelSelectModal in combo creation (decolua#1031)

* fix: React hooks - variable declaration order and lazy initialization (decolua#1017)

- Fixed variable declaration order in CLIToolsPageClient.js (functions before useEffect)
- Added lazy initialization for useState in BasicChatPageClient.js to read from localStorage
- Reduced ESLint errors by ~23%

Co-authored-by: yuangejiaozhu <leegajone@email.com>

* Add Codex GPT 5.5 image support (decolua#991)

* Add linux/arm64 support for docker image (decolua#979)

* fix: handle permission denied when creating DATA_DIR (decolua#1005)

Fallback to default user directory (~/.9router) when configured
DATA_DIR is not writable (EACCES/EPERM). Other errors still throw.

Co-authored-by: Thiên Toán <toanalien@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Add OIDC dashboard auth (decolua#1020)

* Fix

* Update changelog

* TUI Source

* Fix zoom controls contrast in topology view (decolua#1066)

* fix(tray): switch macOS/Linux tray to systray2 fork (decolua#1080)

The legacy `systray@1.0.5` package (last published 2018) bundles a 2017
x86_64 Go binary whose Mach-O headers are rejected by modern dyld (macOS
14+ / Apple Silicon). The result on affected systems was that
`9router --tray` (and "Hide to Tray" from the interactive menu) printed
"Router is now running in system tray" but no menubar icon appeared —
the failure was silently swallowed by `catch (err) { return null }`.

This swaps the runtime tray library for `systray2@2.1.4`, which embeds
the maintained getlantern/systray-portable binaries that work on macOS
14+ under Rosetta. Changes:

- hooks/trayRuntime.js: install `systray2@2.1.4` (not `systray@1.0.5`)
  into ~/.9router/runtime/node_modules. Always purge the legacy systray
  package on every run — its binary is broken on macOS and an AV false
  positive on Windows. chmod +x the bundled Go binary in case the npm
  tarball drops the executable bit (observed on macOS).
- src/cli/tray/tray.js: resolveSystray() now prefers systray2 with a
  fallback to legacy systray for safety. initUnixTray() uses the new
  .ready() promise API, surfaces failures to stderr instead of silently
  returning null, and sets isTemplateIcon:false so the full-color
  icon.png renders correctly (template mode would show a solid white
  square because only the alpha channel is used). killTray() passes
  false to systray2's kill so it doesn't call process.exit(0) before
  the rest of cleanup (server SIGKILL, MITM/tunnel) runs.
- package.json: update the `comment_systray` field to describe the new
  package choice.

Fixes decolua#1079

* fix(ui): replace browser confirm dialogs with ConfirmModal component (decolua#1060)

Replace 10 instances of native browser confirm() dialogs with the
existing ConfirmModal component for consistent UX across the dashboard.

Changes:
- Add ConfirmModal to 5 files (combos, endpoints, proxy pools, providers, connections)
- Maintain same confirmation flow with improved styling
- Use 'danger' variant for destructive actions
- Preserve all existing functionality

Affected areas:
- Combo deletion (combos page)
- API key deletion/pausing (EndpointPageClient)
- Proxy pool management (single/bulk delete, disable dead proxies)
- Provider operations (disable all models, delete connection, delete compatible node)
- Connection management (ConnectionsCard)

All changes manually tested and verified.

* feat: add minimax tts support (decolua#1043)

* fix(docker): restore /app/server.js in standalone build (decolua#1064) (decolua#1067)

Set outputFileTracingRoot back to projectRoot. In Docker, the parent
(monorepoRoot) was /, which caused Next.js to emit server.js at
.next/standalone/app/server.js and pull in /usr, /root, /proc paths,
breaking  from /app.

Fixes decolua#1064

Co-authored-by: Muhammad Ridwan Ramadhan <ridwanramadhan8888@gmail.com>

* # v0.4.36 (2026-05-13)

## Features
- Add MiniMax TTS provider support (decolua#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (decolua#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (decolua#1064, decolua#1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (decolua#1080)
- Fix zoom controls contrast in topology view (decolua#1066)

* Docker

* Fix bug

* Update CHANGELOG.md

* Update changelog

* chore: clean Docker tags + clearer pulls badge

- Workflow: drop sha + major.minor tags, keep only latest + full version
- README: rename "Docker Hub" badge → "Docker pulls" for clarity

* feat: add DeepSeek TUI as CLI tool in dashboard (decolua#1088)

Co-authored-by: Ansh7473 <your-github-email@example.com>

* # v0.4.38 (2026-05-13)

## Features
- Add DeepSeek TUI as CLI tool in dashboard (decolua#1088)

## Fixes
- Fix broken Docker image in v0.4.36/v0.4.37 (decolua#1096, decolua#1097)

## Improvements
- Clean Docker tags + clearer pulls badge

* Fix build bug

* feat: add drag-and-drop reordering for combo models (decolua#1056) (decolua#1108)

* fix(autostart): work on nvm + npm 9/10, actually register with launchctl (fixes decolua#1082) (decolua#1104)

* fix(autostart): resolve cli.js path locally, register with launchctl, verify state

The current `cli/src/cli/tray/autostart.js` is silently broken on every nvm
install (and likely Volta / asdf / Homebrew / user-prefix npm) and on every
npm version >= 9. Enabling auto-start from the tray menu writes a launchd
plist that references a non-existent script; the menu then reports
"✓ Auto-start Enabled" because the existence check only verifies the file is
on disk. On the next OS boot launchd fails with MODULE_NOT_FOUND. None of
this surfaces to the user.

This rewrite addresses the four problems reported in decolua#1082:

1. `npm bin -g` was removed in npm 9. autostart.js called it as a primary
   resolution mechanism and silently fell back to a hardcoded
   `/usr/local/lib/node_modules/9router/cli.js` path on failure. Replaced
   with a `getCliJsPath()` helper that tries (in order): the explicit
   `cliPath` argument, `process.argv[1]` when it's our own cli.js, and a
   path computed relative to autostart.js's own location (since this file
   always lives at `<pkg>/src/cli/tray/autostart.js`, cli.js is three
   levels up regardless of install layout). Returns null on no match.

2. The `/usr/local/...` fallback was outright wrong for nvm/Volta/asdf
   installs. Dropped entirely. If no candidate resolves, return false
   instead of writing a plist pointing at a missing script.

3. `enableMacOS()` never called `launchctl load -w`, only `unload`. The
   plist was therefore inert until the next user login, with no signal to
   the user that anything was wrong. Now it unloads (defensive, in case of
   re-enable) and then loads, so the agent is active in the current session.

4. `isAutoStartEnabled()` on macOS only checked file existence — so the
   tray menu reported "✓ Enabled" even when launchd had the agent in a
   failed state or hadn't loaded it. Now also runs `launchctl list
   ${APP_LABEL}` and only returns true if launchd recognizes the label.

Additional changes for robustness:

- The macOS plist now invokes node + cli.js directly with absolute paths
  instead of wrapping in `zsh -l -c "..."`. The shell-wrapper approach
  depended on the user's login shell sourcing nvm/PATH correctly, which is
  fragile (nvm.sh sourcing varies between users; some setups don't add
  node to PATH from a non-interactive login shell).
- `EnvironmentVariables.PATH` in the plist now explicitly includes node's
  bin directory plus the standard system paths, so child processes spawned
  by cli.js (e.g. the runtime `npm install` calls) can still resolve `npm`
  even under launchd's minimal default env.
- Windows and Linux paths were calling `npm bin -g` with the same fallback
  problem; both now use `getCliJsPath()` consistently. The Windows VBS
  branch is simplified (always run node+cli.js, drop the `9router.cmd`
  lookup that depended on the npm prefix path).
- Removed the unused `getStartCommand()` helper that was never imported.

Fixes decolua#1082

* fix(autostart): skip launchctl unload/load when current process is the agent

When the running 9router cli.js was itself spawned by the autostart launchd
agent (after a reboot when autostart was previously enabled), clicking the
tray menu's "Disable Auto-start" item would unload the agent — and that
unload sends SIGTERM to the running process, killing the click handler and
the tray icon before the menu could flip its label.

Add a small `isAgentSelfMacOS()` probe (parses `launchctl list ${label}` and
compares the PID against `process.pid`). When we're the agent itself,
disable skips the unload (the plist file removal is enough to prevent the
agent from starting on the next login) and enable skips the load (the plist
is already loaded under our own PID, the on-disk update is what matters for
next boot). External callers — e.g. enable from a manually-launched 9router
or from a script — still get the full unload/load behavior they need.

Without this, the tray UX after a reboot was: click Disable -> tray icon
silently disappears, no menu label change. Now: click Disable -> label
flips to "Enable Auto-start", tray stays, plist removed; click Enable again
-> label flips back, plist re-created.

* Fix issue with Ollama usage not being tracked and shown in 9router UI (decolua#1102)

Co-authored-by: Abhi <abhi@fresent.com>

* feat(usage): add Today period option to Usage & Analytics (decolua#1063)

Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).

Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại

Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
  đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
  + getUsageStats: dùng nhánh live history khi period = today,
    cutoff lấy từ 00:00 hôm nay theo local time.
  + getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.

---------

Co-authored-by: Zanuar Tri Romadon <triromadon@gmail.com>
Co-authored-by: @aaronjmars <61592645+aaronjmars@users.noreply.github.com>
Co-authored-by: aeonframework <aeon@aeonframework.dev>
Co-authored-by: decolua <decoluadt@example.com>
Co-authored-by: Anh <tuananhht94@users.noreply.github.com>
Co-authored-by: Tran Hoang Nguyen <tranhoangnguyen03@gmail.com>
Co-authored-by: FlyingMongoose <399379+flyingmongoose@users.noreply.github.com>
Co-authored-by: Jone <132452616+yuangejiaozhu@users.noreply.github.com>
Co-authored-by: yuangejiaozhu <leegajone@email.com>
Co-authored-by: Aleksei <54264559+eng2007@users.noreply.github.com>
Co-authored-by: monlor <20394007+monlor@users.noreply.github.com>
Co-authored-by: Thiên Toán <toanalien@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Walter Cheng <walter.cheng@carleton.ca>
Co-authored-by: Iqbal Muhammad Hasbi <39003120+IzayoiS@users.noreply.github.com>
Co-authored-by: Tri Dung Nguyen <103993527+ntdung6868@users.noreply.github.com>
Co-authored-by: Muhammad Ridwan Ramadhan <34785758+ridwanramadhan@users.noreply.github.com>
Co-authored-by: Muhammad Ridwan Ramadhan <ridwanramadhan8888@gmail.com>
Co-authored-by: YourAnsh <88235820+Ansh7473@users.noreply.github.com>
Co-authored-by: Ansh7473 <your-github-email@example.com>
Co-authored-by: Fresent <102426695+fresent@users.noreply.github.com>
Co-authored-by: Abhi <abhi@fresent.com>
Co-authored-by: Dũng A Tô Ri A <dungartoria@gmail.com>
caothu159 added a commit to diepxuan/9router that referenced this pull request May 14, 2026
* fix: improve dropdown text readability in dark theme on usage page (decolua#997)

- Add global CSS rules for select elements in dark mode
- Use color-scheme property to signal dark mode to browser
- Explicitly style option elements with dark theme colors
- Fix UsageStats dropdown to use correct CSS variables (bg-surface, text-text-main)

Fixes dropdown text being unreadable in dark theme on usage page:
- Provider filter dropdown
- Table view selector (Model/Account/API Key/Endpoint)
- Pagination page size selector

Tested in Chrome and Firefox with both light and dark themes.

* fix(security): scope OAuth callback postMessage targets and re-enable TLS verification on DNS-bypass fetch (decolua#998)

Two findings, neither blocked by anything else:

1. src/app/callback/page.js — the OAuth callback page posted the
   { code, state } payload to window.opener with targetOrigin "*", so any
   page that opened the popup against the well-known redirect_uri received
   the live OAuth code. The expectedOrigins list was already computed but
   never used. Iterate over it and pass the origin per send.

2. open-sse/utils/proxyFetch.js — createBypassRequest() set
   rejectUnauthorized: false on the HTTPS request that runs after the
   Google-DNS-resolved real-IP fallback (used for cloudcode-pa.googleapis,
   GitHub Copilot, Cursor, AWS LLM endpoints). Combined with servername:
   parsedUrl.hostname this gave SNI-correct connections that nonetheless
   ignored cert validation, so an on-path attacker could swap in their
   own cert and read the user's API tokens / prompts. Drop the flag.

Detected by Aeon + semgrep (javascript.browser.security.wildcard-postmessage-configuration
+ problem-based-packs.insecure-transport.js-node.bypass-tls-verification).
Severity: HIGH (#1) / MEDIUM (#2).
CWEs: CWE-1385 (#1), CWE-295 (#2).

Co-authored-by: aeonframework <aeon@aeonframework.dev>

* # v0.4.29 (2026-05-10)

## Features
- Add Cline & Kilo Code tool cards
- Tailscale TUN mode for stable Funnel TLS
- Sort APIKEY providers by usage, collapse to top 20

## Improvements
- Local Material Symbols font (no Google Fonts)
- Docker base: Bun → Node 22-alpine
- MITM reads aliases from JSON cache (no native sqlite)
- Stream stall timeout (2 min) in open-sse

## Fixes
- Fal.ai key test: use stable models endpoint

* Feat : Gitbook

* Gitbook

* Update gitbook

* fix: respect PORT env in internal model-test fetch (decolua#1014)

Internal model test routes fetched 127.0.0.1:UPDATER_CONFIG.appPort
(hardcoded 20128). When PORT env is set to a different value, the app
listens on PORT but the internal fetch still targets 20128, causing
"fetch failed" on /api/models/test and /api/providers/[id]/test-models.

Fall back to UPDATER_CONFIG.appPort only when process.env.PORT is unset.

* fix: normalize developer role to system for OpenAI-format providers (decolua#1011)

Deepseek API (and likely other providers) reject messages with
role: 'developer' — only accept system, user, assistant, tool.
filterToOpenAIFormat() normalizes content blocks but never touched
message roles, so developer passed through unmodified and caused
400 errors (issue decolua#773).

Fix: add one-line developer → system mapping in filterToOpenAIFormat()
before role-specific logic. This is the common normalization point
called for all targetFormat=openai providers (Deepseek, Groq, Mistral,
Perplexity, Together, Fireworks, Cerebras, xAI, NVIDIA, etc.)

Closes decolua#773

* feat(mitm): implement dynamic linux cert resolution and NSS db injection (decolua#1010)

- Replaced hardcoded LINUX_CERT_DIR with dynamic filesystem probing to support Debian, Arch, Fedora, and openSUSE system trust stores.
- Added updateNssDatabases helper to seamlessly inject root certificates directly into browser NSS databases (e.g., ~/.pki/nssdb, ~/.mozilla/firefox).
- Supported standard and snap-based Chrome/Chromium and Firefox installations.
- Made browser cert injection resilient, executing under the current user to prevent file ownership issues, and safely falling back if certutil is absent.

* # v0.4.30 (2026-05-11)
## Features
- MCP stdio→SSE bridge: expose local stdio MCP plugins over SSE (api/mcp/[plugin]/sse, /message)
- Dynamic Linux cert resolution + NSS DB injection (Debian/Arch/Fedora/openSUSE, Chrome/Chromium/Firefox incl. snap) (decolua#1010)
- Cowork tool: expanded settings UI & API
- GitBook docs (DocsContent, DocsLayout)
## Fixes
- OAuth callback postMessage scoped to expected origins (CWE-1385) (decolua#998)
- Re-enable TLS verification on DNS-bypass fetch (CWE-295) (decolua#998)
- Normalize `developer` role → `system` for OpenAI-format providers (Deepseek, Groq, …) (decolua#1011, closes decolua#773)
- Respect `PORT` env in internal model-test fetch (decolua#1014)
- Dropdown text readability in dark theme on usage page (decolua#997)
## Improvements
- Refactor Claude CLI spoof headers into shared constant
- Tool deduper utility in open-sse handlers

* feat(ui): add Done button to ModelSelectModal in combo creation (decolua#1031)

* fix: React hooks - variable declaration order and lazy initialization (decolua#1017)

- Fixed variable declaration order in CLIToolsPageClient.js (functions before useEffect)
- Added lazy initialization for useState in BasicChatPageClient.js to read from localStorage
- Reduced ESLint errors by ~23%

Co-authored-by: yuangejiaozhu <leegajone@email.com>

* Add Codex GPT 5.5 image support (decolua#991)

* Add linux/arm64 support for docker image (decolua#979)

* fix: handle permission denied when creating DATA_DIR (decolua#1005)

Fallback to default user directory (~/.9router) when configured
DATA_DIR is not writable (EACCES/EPERM). Other errors still throw.

Co-authored-by: Thiên Toán <toanalien@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Add OIDC dashboard auth (decolua#1020)

* Fix

* Update changelog

* TUI Source

* Fix zoom controls contrast in topology view (decolua#1066)

* fix(tray): switch macOS/Linux tray to systray2 fork (decolua#1080)

The legacy `systray@1.0.5` package (last published 2018) bundles a 2017
x86_64 Go binary whose Mach-O headers are rejected by modern dyld (macOS
14+ / Apple Silicon). The result on affected systems was that
`9router --tray` (and "Hide to Tray" from the interactive menu) printed
"Router is now running in system tray" but no menubar icon appeared —
the failure was silently swallowed by `catch (err) { return null }`.

This swaps the runtime tray library for `systray2@2.1.4`, which embeds
the maintained getlantern/systray-portable binaries that work on macOS
14+ under Rosetta. Changes:

- hooks/trayRuntime.js: install `systray2@2.1.4` (not `systray@1.0.5`)
  into ~/.9router/runtime/node_modules. Always purge the legacy systray
  package on every run — its binary is broken on macOS and an AV false
  positive on Windows. chmod +x the bundled Go binary in case the npm
  tarball drops the executable bit (observed on macOS).
- src/cli/tray/tray.js: resolveSystray() now prefers systray2 with a
  fallback to legacy systray for safety. initUnixTray() uses the new
  .ready() promise API, surfaces failures to stderr instead of silently
  returning null, and sets isTemplateIcon:false so the full-color
  icon.png renders correctly (template mode would show a solid white
  square because only the alpha channel is used). killTray() passes
  false to systray2's kill so it doesn't call process.exit(0) before
  the rest of cleanup (server SIGKILL, MITM/tunnel) runs.
- package.json: update the `comment_systray` field to describe the new
  package choice.

Fixes decolua#1079

* fix(ui): replace browser confirm dialogs with ConfirmModal component (decolua#1060)

Replace 10 instances of native browser confirm() dialogs with the
existing ConfirmModal component for consistent UX across the dashboard.

Changes:
- Add ConfirmModal to 5 files (combos, endpoints, proxy pools, providers, connections)
- Maintain same confirmation flow with improved styling
- Use 'danger' variant for destructive actions
- Preserve all existing functionality

Affected areas:
- Combo deletion (combos page)
- API key deletion/pausing (EndpointPageClient)
- Proxy pool management (single/bulk delete, disable dead proxies)
- Provider operations (disable all models, delete connection, delete compatible node)
- Connection management (ConnectionsCard)

All changes manually tested and verified.

* feat: add minimax tts support (decolua#1043)

* fix(docker): restore /app/server.js in standalone build (decolua#1064) (decolua#1067)

Set outputFileTracingRoot back to projectRoot. In Docker, the parent
(monorepoRoot) was /, which caused Next.js to emit server.js at
.next/standalone/app/server.js and pull in /usr, /root, /proc paths,
breaking  from /app.

Fixes decolua#1064

Co-authored-by: Muhammad Ridwan Ramadhan <ridwanramadhan8888@gmail.com>

* # v0.4.36 (2026-05-13)

## Features
- Add MiniMax TTS provider support (decolua#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (decolua#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (decolua#1064, decolua#1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (decolua#1080)
- Fix zoom controls contrast in topology view (decolua#1066)

* Docker

* Fix bug

* Update CHANGELOG.md

* Update changelog

* chore: clean Docker tags + clearer pulls badge

- Workflow: drop sha + major.minor tags, keep only latest + full version
- README: rename "Docker Hub" badge → "Docker pulls" for clarity

* feat: add DeepSeek TUI as CLI tool in dashboard (decolua#1088)

Co-authored-by: Ansh7473 <your-github-email@example.com>

* # v0.4.38 (2026-05-13)

## Features
- Add DeepSeek TUI as CLI tool in dashboard (decolua#1088)

## Fixes
- Fix broken Docker image in v0.4.36/v0.4.37 (decolua#1096, decolua#1097)

## Improvements
- Clean Docker tags + clearer pulls badge

* Fix build bug

* feat: add drag-and-drop reordering for combo models (decolua#1056) (decolua#1108)

* fix(autostart): work on nvm + npm 9/10, actually register with launchctl (fixes decolua#1082) (decolua#1104)

* fix(autostart): resolve cli.js path locally, register with launchctl, verify state

The current `cli/src/cli/tray/autostart.js` is silently broken on every nvm
install (and likely Volta / asdf / Homebrew / user-prefix npm) and on every
npm version >= 9. Enabling auto-start from the tray menu writes a launchd
plist that references a non-existent script; the menu then reports
"✓ Auto-start Enabled" because the existence check only verifies the file is
on disk. On the next OS boot launchd fails with MODULE_NOT_FOUND. None of
this surfaces to the user.

This rewrite addresses the four problems reported in decolua#1082:

1. `npm bin -g` was removed in npm 9. autostart.js called it as a primary
   resolution mechanism and silently fell back to a hardcoded
   `/usr/local/lib/node_modules/9router/cli.js` path on failure. Replaced
   with a `getCliJsPath()` helper that tries (in order): the explicit
   `cliPath` argument, `process.argv[1]` when it's our own cli.js, and a
   path computed relative to autostart.js's own location (since this file
   always lives at `<pkg>/src/cli/tray/autostart.js`, cli.js is three
   levels up regardless of install layout). Returns null on no match.

2. The `/usr/local/...` fallback was outright wrong for nvm/Volta/asdf
   installs. Dropped entirely. If no candidate resolves, return false
   instead of writing a plist pointing at a missing script.

3. `enableMacOS()` never called `launchctl load -w`, only `unload`. The
   plist was therefore inert until the next user login, with no signal to
   the user that anything was wrong. Now it unloads (defensive, in case of
   re-enable) and then loads, so the agent is active in the current session.

4. `isAutoStartEnabled()` on macOS only checked file existence — so the
   tray menu reported "✓ Enabled" even when launchd had the agent in a
   failed state or hadn't loaded it. Now also runs `launchctl list
   ${APP_LABEL}` and only returns true if launchd recognizes the label.

Additional changes for robustness:

- The macOS plist now invokes node + cli.js directly with absolute paths
  instead of wrapping in `zsh -l -c "..."`. The shell-wrapper approach
  depended on the user's login shell sourcing nvm/PATH correctly, which is
  fragile (nvm.sh sourcing varies between users; some setups don't add
  node to PATH from a non-interactive login shell).
- `EnvironmentVariables.PATH` in the plist now explicitly includes node's
  bin directory plus the standard system paths, so child processes spawned
  by cli.js (e.g. the runtime `npm install` calls) can still resolve `npm`
  even under launchd's minimal default env.
- Windows and Linux paths were calling `npm bin -g` with the same fallback
  problem; both now use `getCliJsPath()` consistently. The Windows VBS
  branch is simplified (always run node+cli.js, drop the `9router.cmd`
  lookup that depended on the npm prefix path).
- Removed the unused `getStartCommand()` helper that was never imported.

Fixes decolua#1082

* fix(autostart): skip launchctl unload/load when current process is the agent

When the running 9router cli.js was itself spawned by the autostart launchd
agent (after a reboot when autostart was previously enabled), clicking the
tray menu's "Disable Auto-start" item would unload the agent — and that
unload sends SIGTERM to the running process, killing the click handler and
the tray icon before the menu could flip its label.

Add a small `isAgentSelfMacOS()` probe (parses `launchctl list ${label}` and
compares the PID against `process.pid`). When we're the agent itself,
disable skips the unload (the plist file removal is enough to prevent the
agent from starting on the next login) and enable skips the load (the plist
is already loaded under our own PID, the on-disk update is what matters for
next boot). External callers — e.g. enable from a manually-launched 9router
or from a script — still get the full unload/load behavior they need.

Without this, the tray UX after a reboot was: click Disable -> tray icon
silently disappears, no menu label change. Now: click Disable -> label
flips to "Enable Auto-start", tray stays, plist removed; click Enable again
-> label flips back, plist re-created.

* Fix issue with Ollama usage not being tracked and shown in 9router UI (decolua#1102)

Co-authored-by: Abhi <abhi@fresent.com>

* feat(usage): add Today period option to Usage & Analytics (decolua#1063)

Bổ sung lựa chọn Today vào bộ lọc thời gian của trang Usage & Analytics
(trước đây chỉ có 24h, 7D, 30D, 60D).

Khác biệt với 24h:
- 24h: cuộn 24 giờ trước → hiện tại
- Today: cố định từ 00:00 hôm nay (giờ local) → hiện tại

Thay đổi:
- page.js, UsageStats.js: thêm option Today vào danh sách PERIODS,
  đổi grid mobile từ 4 cột sang 5 cột để fit option mới.
- api/usage/stats, api/usage/chart: cho phép giá trị period today.
- usageRepo.js:
  + getUsageStats: dùng nhánh live history khi period = today,
    cutoff lấy từ 00:00 hôm nay theo local time.
  + getChartData: thêm 24 bucket theo giờ từ 00:00 → 23:59 hôm nay.

---------

Co-authored-by: Zanuar Tri Romadon <triromadon@gmail.com>
Co-authored-by: @aaronjmars <61592645+aaronjmars@users.noreply.github.com>
Co-authored-by: aeonframework <aeon@aeonframework.dev>
Co-authored-by: decolua <decoluadt@example.com>
Co-authored-by: Anh <tuananhht94@users.noreply.github.com>
Co-authored-by: Tran Hoang Nguyen <tranhoangnguyen03@gmail.com>
Co-authored-by: FlyingMongoose <399379+flyingmongoose@users.noreply.github.com>
Co-authored-by: Jone <132452616+yuangejiaozhu@users.noreply.github.com>
Co-authored-by: yuangejiaozhu <leegajone@email.com>
Co-authored-by: Aleksei <54264559+eng2007@users.noreply.github.com>
Co-authored-by: monlor <20394007+monlor@users.noreply.github.com>
Co-authored-by: Thiên Toán <toanalien@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Walter Cheng <walter.cheng@carleton.ca>
Co-authored-by: Iqbal Muhammad Hasbi <39003120+IzayoiS@users.noreply.github.com>
Co-authored-by: Tri Dung Nguyen <103993527+ntdung6868@users.noreply.github.com>
Co-authored-by: Muhammad Ridwan Ramadhan <34785758+ridwanramadhan@users.noreply.github.com>
Co-authored-by: Muhammad Ridwan Ramadhan <ridwanramadhan8888@gmail.com>
Co-authored-by: YourAnsh <88235820+Ansh7473@users.noreply.github.com>
Co-authored-by: Ansh7473 <your-github-email@example.com>
Co-authored-by: Fresent <102426695+fresent@users.noreply.github.com>
Co-authored-by: Abhi <abhi@fresent.com>
Co-authored-by: Dũng A Tô Ri A <dungartoria@gmail.com>
caothu159 pushed a commit to diepxuan/9router that referenced this pull request May 26, 2026
caothu159 pushed a commit to diepxuan/9router that referenced this pull request May 26, 2026
## Features
- Add MiniMax TTS provider support (decolua#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (decolua#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (decolua#1064, decolua#1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (decolua#1080)
- Fix zoom controls contrast in topology view (decolua#1066)
luckystart79-lang pushed a commit to luckystart79-lang/minirouter that referenced this pull request May 27, 2026
luckystart79-lang pushed a commit to luckystart79-lang/minirouter that referenced this pull request May 27, 2026
## Features
- Add MiniMax TTS provider support (decolua#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (decolua#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (decolua#1064, decolua#1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (decolua#1080)
- Fix zoom controls contrast in topology view (decolua#1066)
bemodestdoteth pushed a commit to bemodestdoteth/9router that referenced this pull request Jun 10, 2026
bemodestdoteth pushed a commit to bemodestdoteth/9router that referenced this pull request Jun 10, 2026
## Features
- Add MiniMax TTS provider support (decolua#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (decolua#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (decolua#1064, decolua#1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (decolua#1080)
- Fix zoom controls contrast in topology view (decolua#1066)
Shinzzyak pushed a commit to Shinzzyak/VansRouter that referenced this pull request Sep 11, 2026
Shinzzyak pushed a commit to Shinzzyak/VansRouter that referenced this pull request Sep 11, 2026
## Features
- Add MiniMax TTS provider support (decolua#1043)
- Docker images now published on both GHCR & Docker Hub (decolua/9router) — pull from your preferred registry

## Improvements
- Replace browser confirm dialogs with custom ConfirmModal (decolua#1060)

## Fixes
- Fix Docker `Cannot find module 'next'` error in standalone build
- Restore /app/server.js in Docker standalone build (decolua#1064, decolua#1067)
- Fix CLI TUI menu arrow-key escape sequences leaking (^[[A^[[B)
- Switch macOS/Linux tray to systray2 fork (fixes Kaspersky AV false-positive) (decolua#1080)
- Fix zoom controls contrast in topology view (decolua#1066)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants