Commit Graph
807 Commits
Author SHA1 Message Date
dea4de45fd fix(test): add qoder to PLUS_EXTRA_CLIPROXY_PROVIDERS test expectation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-21 15:19:57 +08:00
Simon Shi 1e60fe186e feat(cliproxy): add Qoder to channel catalog, model routing, and UI
Add Qoder provider to integration layers and dashboard UI:
- Channel-to-provider mapping in catalog-cache
- Model routing owner hints
- Provider editor owner map
- Setup wizard provider list
- Fallback visuals and brand color
2026-05-21 03:06:18 +09:00
Tam Nhu Tran 8ad7653a2e feat(dashboard): add Korean language support
Adds Korean (ko) as a supported dashboard locale alongside en, zh-CN, vi, ja.
Browser locales starting with `ko` (e.g. `ko-KR`) normalize to `ko` and the
Language switcher exposes "Korean" / "한국어" as a selectable option.

The full ~2670-key translation block was translated from the English source
with the formal-polite register (하십시오체) appropriate for an enterprise
dashboard. Technical names (CCS, Claude, CLIProxy, OAuth, provider names,
env vars, paths) are kept untranslated. All placeholders (e.g. {{name}},
{{count}}, {{provider}}) are preserved verbatim, matching the parity test.

Also backfills 23 keys missing from zh-CN, vi, and ja that were added to en
with the recent "Shared Resource Controls" feature (#1206 -> 2a422b3b):
accountsPage.resources*, accountsTable.{sharedResources,resourcesTitle,
resourcesShared,resourcesProfileLocal,badges.profileLocal},
commonToast.resourcesUpdated, and the full editAccountSharedResources
section. Without this backfill the existing i18n parity test was red for
all non-en locales on dev.

Closes #1241
2026-05-14 19:14:13 -04:00
Tam Nhu Tran 15a1faf3ab fix(ui): handle TOML triple-quotes in arrays and AST-aware JSON keys
PR-Agent on #1248 flagged two remaining sensitive-mask edge cases:

1. The TOML array walker stepped through triple-quoted strings one
   single quote at a time. With a body containing both quotes and
   structural characters, like """abc"]xyz""", a stray `]` inside the
   string body could escape the string-skip pair and terminate the
   array scan early. Add skipTomlString that recognizes triple-quoted
   strings ("""…""", '''…''') and skips them as one unit.

2. The JSON regex matched any "...":  pattern, so substrings inside
   escaped value strings, like "description": "use \\"API_KEY\\": header",
   could mask unrelated text. Replace the JSON branch with a state-aware
   scanner that only registers a string as a key when it sits at an
   object-key position (preceded by `{` or `,`, followed by `:`).

Adds regression tests for both cases.
2026-05-14 18:10:26 -04:00
Tam Nhu Tran a633ee0af4 fix(ui): skip TOML comments when scanning sensitive array values
PR-Agent on #1248 flagged that the TOML array walker did not skip
`#` line comments while balancing brackets. A sensitive array with a
comment containing `]` could terminate the scan early and leave the
real tail of the secret unmasked. Skip from `#` to the next newline at
the array's top level. Adds a regression test.
2026-05-14 17:57:06 -04:00
Tam Nhu Tran f404e52439 perf(ui): tighten sensitive-mask decoration scope and reveal selector
PR-Agent review on #1248 flagged two follow-ups:

1. The reveal CSS used `.cm-editor.cm-sensitive-revealed`, but
   @uiw/react-codemirror puts the consumer className on its outer
   wrapper, not on .cm-editor. The selector never matched, so the Eye
   toggle could not un-blur masked values. Drop `.cm-editor` from the
   selector so it matches the wrapper that actually carries the class.

2. The sensitive-decoration plugin rebuilt on every `viewportChanged`,
   which means each scroll event re-stringified and rescanned the
   entire document even though decorations are doc-only-dependent.
   Rebuild only on `docChanged` to remove scroll-time work.
2026-05-14 17:50:50 -04:00
Tam Nhu Tran bac51ce1ff fix(ui): honor TOML quoted keys and escaped quotes in sensitive masking
Two remaining gaps in the sensitive-value detector flagged by PR-Agent
on #1248:

1. The TOML/YAML key regex only matched bare keys, so values written
   under quoted keys like `"API_KEY" = "secret"` were never masked.
   Accept basic-string and literal-string quoted keys and normalize the
   captured key (strip quotes, unescape) before isSensitiveKey.

2. The string-skip helper used indexOf for the next quote character, so
   a value like `AUTH_TOKEN = ["a \"quoted\" value", "x"]` could
   terminate scanning early and leak unrelated text or under-mask the
   secret. Introduce skipString that honors backslash escapes for basic
   strings and is used by both the TOML array walker and the JSON
   balanced-brace walker.

Adds regression tests for both edge cases.
2026-05-14 17:44:52 -04:00
Tam Nhu Tran eefb76214e fix(ui): mask multi-line sensitive values in CodeEditor
Extend the sensitive-key decoration to cover the full logical value, not
just the rest of the current line. Adds value-extent detection for:

- TOML triple-quoted strings (multi-line literal/basic strings)
- TOML arrays ([ ... ] across multiple lines, string-aware)
- YAML block scalars (| and > with indent-based termination)
- JSON arrays and objects (balanced braces, string-aware)

Without this, a secret value spanning multiple lines (multi-line API
key, certificate, private key) was only blurred on its opening line and
the rest stayed readable when the Eye toggle was masked.

Adds regression tests for the TOML triple-quote and array cases.
Flagged by PR-Agent review on #1248.
2026-05-14 17:38:06 -04:00
Tam Nhu Tran 420494e0e0 fix(ui): make codex config.toml viewer selectable
Replace the hand-rolled textarea+Prism overlay in CodeEditor with
CodeMirror 6. The overlay hid the textarea selection with
`selection:bg-transparent` and `WebkitTextFillColor: transparent` as a
workaround for wrap drift between the highlight layer and the textarea,
which left Ctrl+A / drag-select / Ctrl+C with no visible result.

CodeMirror handles selection, copy, undo, and wrap natively. Custom
themes match the previous prism-react-renderer github (light) and
nightOwl (dark) palettes; background is transparent so the surrounding
bg-muted/30 still shows; font, font-size, line-height, padding, and
gutter-less layout are preserved.

Public CodeEditor props are unchanged (value, onChange, language,
readonly, exactText, className, minHeight, heightMode) so all seven
call sites keep working. The sensitive-key blur is reimplemented as a
CodeMirror Decoration and stays gated behind the existing Eye toggle.

Test setup gets a Range.getClientRects polyfill so CM mounts cleanly in
JSDOM, and shared-page.test.tsx checks .cm-content text directly.

Closes #1247
2026-05-14 17:22:26 -04:00
Kai (Tam Nhu) TranandGitHub 4748c452bd fix(codex): self-heal ccsxp cliproxy provider (#1243) 2026-05-14 12:35:31 -04:00
Kai (Tam Nhu) TranandGitHub ac333dd1d5 fix: clarify CLIProxy setup actions
Make account setup the primary CLIProxy CTA, keep advanced variant creation secondary, and contain setup dialogs across narrow viewports.
2026-05-12 19:23:59 -04:00
Kai (Tam Nhu) TranandGitHub 5138741b5c fix(ui): keep codex toml tokens inline (#1219) 2026-05-12 07:51:59 -04:00
Kai (Tam Nhu) TranandGitHub ba761a0d1e fix(ui): wrap codex config highlight editor (#1217) 2026-05-11 21:58:55 -04:00
Kai (Tam Nhu) TranandGitHub e1777247b3 fix(ui): restore codex config highlighting (#1216) 2026-05-11 21:33:33 -04:00
Kai (Tam Nhu) TranandGitHub 225401ceca fix(ui): render codex config as exact text (#1215) 2026-05-11 20:43:26 -04:00
Kai (Tam Nhu) TranandGitHub 29da75c0d4 fix(cliproxy): explain headless Codex OAuth recovery
Closes #1213
2026-05-11 14:25:34 -04:00
Kai (Tam Nhu) TranandGitHub 248b6b735d Merge pull request #1200 from kaitranntt/kai/feat/1199-shared-resource-controls
feat(auth): add shared resource controls
2026-05-10 22:01:57 -04:00
Tam Nhu Tran 497bca8684 fix(ui): surface Plus OAuth credential diagnostics in Add Account dialog
When POST /api/cliproxy/auth/:provider/start-url returns 400
plus_oauth_credentials_missing or 502 plus_oauth_url_missing_client_id,
surface the server-provided human-readable message (env var names and
backend=original switch hint) instead of the raw error code. Error
propagates through the existing toast/inline-alert path in
add-account-dialog.tsx — no new component primitives.

Refs #1208
2026-05-10 21:55:08 -04:00
Kai (Tam Nhu) TranandGitHub 35346a981f fix(ui): keep profile dialog actions visible on small screens (#1204) 2026-05-09 14:20:06 -04:00
Tam Nhu Tran 8b34060294 fix(dashboard): show real shared plugin registry state 2026-05-09 03:18:20 -04:00
Tam Nhu Tran 2a422b3bd3 feat(dashboard): add shared resource controls 2026-05-09 02:29:08 -04:00
Kai (Tam Nhu) TranandGitHub 74d73748ee feat: support Codex fast service-tier aliases
* feat: support Codex fast service-tier aliases

* fix: send Codex fast tier as priority
2026-05-07 15:34:05 -04:00
Kai (Tam Nhu) TranandGitHub 19a50a8dd8 feat: deprecate GitHub Copilot compatibility surfaces (#1196) 2026-05-07 13:06:03 -04:00
Kai (Tam Nhu) TranandGitHub 8b681df455 fix: route Cursor auth through browser polling
Closes #1194
2026-05-07 11:25:17 -04:00
Kai (Tam Nhu) TranandGitHub be9effcce3 feat: clarify account history sync route (#1187)
* feat: clarify account history sync route

* fix: clarify shared context command examples

* fix: report missing bare profile settings
2026-05-05 13:33:04 -04:00
Tam Nhu Tran 58af9cd501 fix(ui): surface Claude Opus 4.7 in Claude picker 2026-05-05 11:38:00 -04:00
Kai (Tam Nhu) TranandGitHub 923683bf30 feat(cliproxy): route plus dashboard to maintained fork (#1173) 2026-05-03 13:01:04 -04:00
Kai (Tam Nhu) TranandGitHub 50b0ffba75 fix(cliproxy): refresh upstream model and quota surfaces (#1158)
* fix(cliproxy): refresh upstream model and quota surfaces

* test(cliproxy): cover codex legacy model fallback
2026-05-01 02:19:31 -04:00
Tam Nhu Tran 7f086c8685 fix(ui): drop dedicated stage column; render stage chip inline in message
Adding a separate STAGE column squeezed MESSAGE to 0px at the
default 22/52/26 panel split — fixed columns + gaps totalled more
than the list panel's available width.

Render the stage chip inline at the start of the MESSAGE column when
present, restoring the original 7-column grid. The bot's earlier
"extra cell mismatch" complaint is still satisfied because there's
no longer a row-only cell missing from the header.

Also tighten three fixed columns to give MESSAGE more breathing room
even on narrower saved panel layouts:
- Module 140 -> 120
- Latency 72 -> 64
- Request 112 -> 100
2026-04-30 16:59:02 -04:00
Tam Nhu Tran e7058b6b6d fix(ui): include stage in leaf coalesce key
PR-Agent flagged that the leaf coalesceKey omitted `stage`, so two
adjacent no-requestId logs differing only by stage (e.g. one logging
`route` and the next `dispatch`) would merge into a single `×2`
row, hiding a distinct entry.

Add `stage` to the coalesce tuple. New unit test asserts that two
leaves identical except for stage stay as separate rows.

Refs #1138, #1151
2026-04-30 16:31:31 -04:00
Tam Nhu Tran 2ddc5d5afd fix(ui): remove intra-trace coalescing — preserve every stage for inspection
PR-Agent flagged that the trace-child coalesce was hiding legitimate
repeated stage emissions: a request that retries an upstream call or
emits the same stage twice for any reason would show only the first
occurrence behind a `×N` badge, with subsequent attempts no longer
selectable or inspectable.

The original noise problem motivating intra-trace coalesce was the
149-stage `web-server:http` self-polling trace, but that's already
hidden by default via the `hideDashboardInternals` filter. Users who
opt in to see internals are deliberately debugging the dashboard and
SHOULD see all 149 stages — collapsing them defeats the opt-in.

Drop `coalesceChildren` entirely. Trace children render uncoalesced
so retries, duplicated stages, and multi-attempt traces stay
individually inspectable. Standalone-leaf coalesce stays — that's
the actual user-facing dedup the PR was about.

Tests: drop the 7 `coalesceChildren` cases; keep 16 covering
`deriveStageHint` and `deriveTraceGroups`.

Refs #1138, #1151
2026-04-30 16:28:42 -04:00
Tam Nhu Tran 6ada2b243e test(ui): cover trace-coalesce, leaf-coalesce, and stage-hint helpers
PR-Agent flagged "No relevant tests" each round. Add 23 unit tests
covering the pure-function surface introduced by this PR:

- deriveStageHint — explicit stage wins, fallback to last .-segment
  of event, 12-char cap, undefined when nothing meaningful.
- coalesceChildren — empty input, single child, adjacent identical
  collapse, distinct messages stay separate (round-1 fix), distinct
  sources stay separate (round-2 fix), distinct stages stay separate,
  interleaving breaks the run.
- deriveTraceGroups — empty input, lone leaf, requestId grouping,
  ts-asc child sort, group ts pinned to oldest child, adjacent leaf
  coalesce with collapsedRange, leaves split by trace stay distinct
  (round-3 fix), distinct messages stay distinct, reverse-chronological
  display sort, max-level + total-latency aggregates, original-input
  adjacency preserved despite display sort.

Refactor: coalesceChildren extracted from logs-trace-row.tsx into
derive-trace-groups.ts so it's testable as a pure function without
React rendering.

Refs #1138, #1151
2026-04-30 16:24:19 -04:00
Tam Nhu Tran 40cc62a67f fix(ui): reserve stage column on every row for consistent alignment
PR-Agent flagged that trace child rows rendered an extra stage-hint
cell while the header grid template only had 7 columns. Result: every
expanded child shifted module/message/latency/request rightward, and
the stage chip overlapped its neighbour in the trace timeline.

Add a dedicated 72px "Stage" column to the header grid template and
to every row variant. Standalone leaves and trace heads render an
empty span at the same width; trace children with stage hints render
the chip in that slot. Column edges now match across all row types.

Refs #1138, #1151
2026-04-30 16:09:50 -04:00
Tam Nhu Tran 04ddc33518 fix(ui): make trace chevron actually collapse auto-expanded traces
The first click on a trace's chevron did nothing visible when the
trace's child was the currently-selected entry. Root cause: `toggle`
only checked the `expanded` set; auto-expand kept re-injecting the
requestId so the row stayed open even after the user added it to
`expanded` (and oscillating subsequent clicks).

Replace the single `expanded` set with a tristate driven by two
sets — `userExpanded` (explicitly opened) and `userCollapsed`
(explicitly closed). Auto-expand only fires when the id is in
neither, so a click on an auto-expanded chevron now writes the
collapse intent to `userCollapsed` and the row collapses on the
first click.

Refs #1138, #1151
2026-04-30 15:58:48 -04:00
Tam Nhu Tran 871b6826f7 fix(ui): coalesce leaves only when truly adjacent in original stream
PR-Agent flagged that the previous derive-trace-groups split entries
into trace and leaf buckets *before* coalescing leaves. That meant two
identical no-requestId entries separated by other rows in the real
stream would still merge into a single `×N` row, hiding signal.

Walk `entries` in input order with single-pass coalescing: a leaf
only extends the previous leaf when no other entry (trace child or
otherwise) appeared between them. Trace children still gather across
interleaving since they're identified by `requestId`. Group children
get sorted ts-ascending for trace-timeline display, with the group's
positional ts pinned to the oldest child for the reverse-chrono
display sort.

Refs #1138, #1151
2026-04-30 15:45:02 -04:00
Tam Nhu Tran 6f1396cf1e fix(ui): import MouseEvent type explicitly in logs-row
PR-Agent flagged that `React.MouseEvent` referenced an unimported
`React` namespace; in stricter TypeScript builds this would fail
typecheck. Import the type alias from `react` directly.

Refs #1138, #1151
2026-04-30 15:44:36 -04:00
Tam Nhu Tran 39ec84a8a9 fix(ui): only show "Copied" feedback when clipboard write succeeds
PR-Agent re-review flagged that `copyText()` swallowed clipboard
errors but the row's `setJustCopied(true)` always fired regardless,
so users in insecure contexts (or with denied clipboard permission)
saw a fake success indicator while nothing was actually copied.

Make `copyText()` return a boolean and gate the "Copied" UI state
on it. When the clipboard write fails, the button stays in its idle
state and the user can retry rather than being misled.

Refs #1138, #1151
2026-04-30 15:37:50 -04:00
Tam Nhu Tran 75cdc02c1f fix(ui): include source in trace child coalesce key
PR-Agent re-review flagged that adjacent trace rows from different
services with the same event/message/stage/level/module would still
merge because `source` was missing from the coalesce key. A request
that fans out across multiple services would lose the second service's
row behind a `× 2` badge.

Add `source` to the trace child coalesce key so each participating
service stays visible in the trace timeline.

Refs #1138, #1151
2026-04-30 15:37:21 -04:00
Tam Nhu Tran b7cfbd14e2 fix(ui): make logs row keyboard-accessible without nested interactive markup
PR-Agent flagged that the row's copy-requestId widget was a
`<span role="button" tabIndex={-1}>` nested inside a row `<button>`.
Two issues:
1. Nested interactive elements inside a `<button>` is invalid HTML.
2. `tabIndex={-1}` made the copy widget unreachable via keyboard, so
   the new affordance was effectively mouse-only.

Convert the row's outer element from `<button>` to `<div role="row"
tabIndex={0}>` with `onKeyDown` for Enter/Space → select. Now the row
itself is keyboard-focusable and the copy widget can be a real
`<button>` with focus-visible styling — keyboard users can Tab to it
and Enter to copy the requestId.

Refs #1138, #1151
2026-04-30 15:26:29 -04:00
Tam Nhu Tran a465e3fec1 fix(ui): include message in coalesce key so distinct entries stay visible
PR-Agent flagged that the leaf and trace-child coalesce keys collapsed
on `(event, module, level, requestId, source)` which merges adjacent
entries that share that tuple but report different content (e.g.
`User logged in: alice` vs `User logged in: bob` would appear as a
single `× 2` row, hiding bob).

Add `message` to both coalesce keys so distinct content stays
distinct. Skip `latencyMs` and `metadata` — those drift per request
even on truly redundant polls and would defeat the dedup the feature
exists for.

Refs #1138, #1151
2026-04-30 15:26:06 -04:00
Tam Nhu Tran 0dfb7b8583 fix(ui): reserve leading chevron column so trace and leaf rows align
Trace rows pushed the TIME column ~30px right because the expand chevron
was an inline flex child while leaf rows had nothing in that position.
Result: every column drifted right whenever a trace row was visible.

Reserve a 16px slot at the start of every row (header + leaf + trace).
Leaf rows render an empty span; trace rows render the chevron centered
inside the slot. All columns now share the same x-coordinates regardless
of row type.

Refs #1138
2026-04-30 14:56:32 -04:00
Tam Nhu Tran 4a77251021 feat(ui): hide dashboard self-polling internals via default filter
On real backend, the logs page was drowning in dashboard self-polling
noise — every `/api/logs` poll emitted a 149-stage `web-server:http`
trace, burying real provider activity.

- New workspace state `hideDashboardInternals` defaults ON. Entries
  whose `source` matches `/^web-server:/i` are filtered client-side
  before they reach the list.
- Toggle exposed in advanced filters as a labelled checkbox/switch with
  a one-line description, so users can opt in to see internals when
  debugging the dashboard itself.
- `clearAdvancedFilters` resets the toggle back to ON to keep the
  default signal-clean experience.
- Header stat strip (entries / traces / errors) wired through the shell.

Backend-side `web-server:*` instrumentation stays intact (still useful
for ops debugging) — just hidden from the user-facing log feed by
default.

Refs #1138, #1141, #1142
2026-04-30 14:31:32 -04:00
Tam Nhu Tran e04598eef7 feat(ui): align logs page visual language with dashboard markers
Logs page was visually disconnected from the rest of the dashboard. Health
uses ornamental `HEALTH.ATTENTIONREQUIRED` markers, Home uses `LIVE
Account Monitor` + stat cards. Logs shipped with a 14px h1 + clinical
table that felt like a different product.

- Header now opens with a `LOGS.STREAM` mono-uppercase marker matching
  the dashboard's `HEALTH.X` style, plus a 16px "Live activity" title
  and a status pill.
- Below the header, a stat strip mirrors the home page's monitor layout:
  ENTRIES / TRACES / ERRORS counters, errors highlighted red when >0.
- Trace row + child row font sizes lifted from 11px to 12-13px;
  request-id column widened to 112px to match the standalone-row table.
- Stage-hint fallback derived from event names so the trace timeline
  still renders meaningful chips when backend entries lack an explicit
  `stage` field (e.g. dashboard self-polling).
- Intra-trace coalesce: identical consecutive child rows collapse to a
  single row with `× N` badge so a 149-stage self-poll trace renders as
  3 rows of signal instead of 149 rows of noise.

Refs #1138, #1141, #1142
2026-04-30 14:30:54 -04:00
Tam Nhu Tran d5b14bec1c fix(ui): align logs row and detail panel via shared accessors
Both surfaces now consume `getDisplayModule / getDisplayStage /
getDisplayRequestId / getDisplayLatency` from `utils.ts`, replacing the
previous divergence where the list row fell back to `source` while the
detail panel showed em-dash placeholders for the same entry.

Also widens the request-id column in the row table from 88px to 112px
and adds a hover-revealed clipboard icon for one-click copy of the full
requestId. List header bumped to 12px sans-serif uppercase + 9h height
for legibility.

Refs #1138, #1141, #1142
2026-04-30 13:37:02 -04:00
Tam Nhu Tran a10e570310 test(ui): cover logs page redesign with virtuoso-mocked render
Three component tests under jsdom: skeleton render, search filter wiring,
row-click -> detail-panel-header flow. react-virtuoso is mocked at module
level (vi.mock) so jsdom can render rows without layout measurements.
Production behavior unchanged.

Refs #1142, #1138
2026-04-30 13:04:21 -04:00
Tam Nhu Tran ae3240d4c8 feat(ui): redesign dashboard logs page with virtualized 3-pane shell
Replace the ornamental logs page (radial-gradient overlays, faux-HUD
captions, scale-on-click animations, two competing tab shells, zero
virtualization) with a calm, accessible, performant surface.

- Calm 48px header + tabs + 3-pane shell (filters | list | detail)
- List virtualized via react-virtuoso; trace-grouped rows by requestId
  with expandable per-stage timeline and per-stage latencyMs
- Filters split into primary (search / level / source) + advanced
  (module / stage / requestId / time-window) with 250ms debounce and
  in-flight react-query cancellation on filter change
- Live-tail pause / resume with rotation-safe id-set diff for the
  new-entries pill; visibility-aware polling
- Detail panel: Overview / Context / Raw tabs; Copy JSON, Copy requestId,
  Show-trace jump; redaction-aware rendering
- Designed empty / loading / error states (4 empty variants, retry on error)
- Keyboard nav (j / k / Enter / Esc / / / Space / ?) with visible focus
  rings; reduced-motion respected; AA-tuned level palette
- New header button for keyboard-shortcuts discoverability (alongside ?
  keypress) -- pairs with the shortcuts dialog
- Optional fields added to LogsEntry to consume the #1141 contract; UI
  ships against fixture (?mock=logs URL flag) so backend isn't a hard
  prerequisite. Dev-only assertLogsEntryShape warns once on drift
- Deleted dead logs-overview-cards.tsx (was unmounted)

Closes #1142
Refs #1138, #1141
2026-04-30 13:03:47 -04:00
Kai (Tam Nhu) TranandGitHub 54e15c430e Merge pull request #1140 from kaitranntt/kai/feat/1139-claude-quota-bars
feat(ui): clarify Claude quota bars
2026-04-30 10:58:11 -04:00
Tam Nhu Tran 476c6d827f fix(ui): improve split quota bar readability 2026-04-30 10:32:43 -04:00
Tam Nhu Tran a8b686d643 feat(ui): show split Claude quota bars 2026-04-30 10:05:19 -04:00
Tam Nhu Tran 804b1e80b4 chore: remove build artifacts accidentally committed 2026-04-29 17:20:11 -04:00