Skip to content

feat(ui): responsive dashboard with quota reset times - #19

Closed
jphein wants to merge 3 commits into
KarpelesLab:masterfrom
jphein:feat/ui-responsiveness
Closed

jphein wants to merge 3 commits into
KarpelesLab:masterfrom
jphein:feat/ui-responsiveness

Conversation

@jphein

@jphein jphein commented Jun 1, 2026

Copy link
Copy Markdown

Summary

Brings the web dashboard (/teamclaude/ui) to a polished, responsive state and surfaces quota reset times alongside utilization.

  • Responsive dashboard — fluid card grid with container queries; layout adapts down to narrow cards (08c80a7).
  • Reset-time display — each 5h/7d quota bar now shows when its window rolls over (↺ HH:MM within a day, weekday + time beyond), with the full timestamp on hover. Reads unified5hReset/unified7dReset (epoch-ms from Anthropic rate-limit headers) already exposed by getStatus(), with a fallback to legacy resetsAt (444281e).
  • Fast shutdown + live HTML reload — server reads web/index.html from disk per request (Cache-Control: no-store), so dashboard edits are live without a restart (08c80a7).

Pre-Landing Review

No issues found. Verified the reset feature is wired end-to-end: backend stores reset timestamps as epoch-ms (account-manager.js:173-174), getStatus() forwards the full quota object via spread (account-manager.js:340), and the frontend fmtReset(ms) consumes ms directly and guards null/past values. Units consistent on both sides. Secret scan clean.

Test plan

  • No automated test suite in this project (package.json defines only start).
  • Manual: open http://localhost:3456/ui against a running proxy with active accounts and confirm reset labels render next to each bar and resize cleanly on a narrow window.

Generated with Claude Code

jphein and others added 3 commits May 22, 2026 10:29
Out-of-band control surfaces for when teamclaude runs headless (systemd, container) and the interactive TUI never renders.

- POST /teamclaude/switch — pin the active account by name
- POST /teamclaude/threshold — change rotation cutoff (0..1), persisted
- GET /ui — single-page dashboard polling /teamclaude/status every 2s
- teamclaude switch <name> + teamclaude threshold <value> CLI wrappers
- Pathname-based routing (handles query strings) + lazy in-memory HTML cache (per Gemini review)
Three dev-ergonomics improvements bundled together:

- src/web/index.html: container queries + clamp() typography +
  auto-fill grid so the dashboard adapts cleanly to narrow Wave
  blocks. Adds .top/.name-text wrappers + ellipsis to keep long
  account names from blowing out the card layout.
- src/server.js: remove HTML cache so /ui reads from disk every
  request. Dev iteration on the dashboard no longer requires a
  daemon restart. The 'no-store' header was already there; the
  in-memory cache was the actual blocker.
- src/index.js: unified SIGINT/SIGTERM handler that calls
  server.closeAllConnections?.() before server.close() and adds
  a 2s safety-net setTimeout(..., 2000).unref() for hard exit.
  Fixes restart hang when an in-flight Claude stream was holding
  the socket open past systemd's TimeoutStopUSec.
Add a reset-time column to each 5h/7d quota bar so the dashboard shows
when each window rolls over, not just current utilization. Reads the
unified5hReset/unified7dReset (epoch-ms from Anthropic rate-limit
headers) already exposed by getStatus(); falls back to legacy resetsAt.
Times render compactly (HH:MM within a day, weekday otherwise) with the
full timestamp on hover, and the bars grid gains a responsive variant
for narrow cards. Bump to 1.0.7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@MagicalTux

Copy link
Copy Markdown
Member

I'm not sure the web dashboard adds much compared to the TUI, do you have a rationale for adding it?

@jphein

jphein commented Jun 26, 2026

Copy link
Copy Markdown
Author

Probably a silly reason, but I run team Claude as a service for my lan. When made into a systemd service I can no longer use the tui. And also I can view the website via other hosts using the proxy on the lan.

@wolph

wolph commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Seems like a very useful addition to me. I was actually considering building something like this myself.

In my case it's because teamclaude runs as a service so I don't have any access to the TUI interface and teamclaude status shows very few details (when was it last updated? is probing enabled? how much time till the next probe?)

@MagicalTux

Copy link
Copy Markdown
Member

Closing as stale: this branch conflicts with master and predates substantial changes (e.g. it assumes no automated test suite exists, which is no longer true). Please rebase and open a fresh PR if the responsive dashboard work is still desired.

@MagicalTux MagicalTux closed this Jul 4, 2026
euisan added a commit to euisan/teamclaude that referenced this pull request Oct 1, 2026
* feat(tui): fold account rows below 70 columns so usage bars wrap

On a narrow terminal (a phone over a remote session, say) an account row
could not hold its name, status and quota bars on one line. The list dropped
every bar past the first and shrank that one to a cell or two, so the weekly
and family windows vanished below 70 columns and the session bar became
unreadable near 40.

Below LIST_MIN a full-width list now folds each account into a heading
(marker, route cells, name, type, status) and one line per quota bar, with
the trailing tags wrapped a whole tag at a time beneath them. Each folded
bar takes the row less a 2-column right margin (COMPACT_BAR_RIGHT_MARGIN),
so it never touches the screen edge. The heading gives the name every
column the rest leaves, measured in display columns, so a CJK name is cut
where it reaches the type column. The smallest drawable terminal drops from
40x8 to 30x8 so the folded layout is reachable at phone widths.

This ports teamcodex KarpelesLab#21 (7296e40, compact layout below 70 columns) and
KarpelesLab#23 (0a34280, compact bar right margin), adapted to teamclaude's budgeted
_listLayout: the layout flags `narrow`, _renderRow passes the width, and
_renderAcct returns lines instead of one string. Split panes are never
narrow. teamcodex KarpelesLab#12/KarpelesLab#17/KarpelesLab#18/KarpelesLab#19 are already in 1.1.22 and were not needed.

At 70 columns and wider the frame is byte-identical to 1.1.22: the trailing
tags moved into a list but are appended in the same order with the same
separator. test/fixtures/tui-wide-frames.json holds frames recorded from the
unmodified renderer, and test/tui-narrow-bars.test.js pins both sides.

docs/teamcodex-port-plan.md classifies all 34 teamcodex commits for later
porting; docs/evidence/ holds the real TUI.start() paint at 30-80 columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(tui): use a neutral double-width name in the narrow-layout fixtures

The width tests need an account name of East Asian Wide characters, but the
one they used read like a real person's name and address. It is now
'가나다라마바사-wide@example.test': seven Hangul syllables (fourteen columns)
then ASCII, which still exercises the cases the tests care about.

- At 40 columns the name is cut exactly where it reaches the type column.
- At 30 columns a fifth syllable would straddle the edge, so it is dropped
  whole and its leftover column padded (a case the old name did not hit).
- At 69 columns the whole name fits.

The wide-frame fixture was re-recorded from the unmodified 1.1.22 renderer
with the new name. The pty capture in docs/evidence was regenerated from the
real TUI.start() paint. The comments that named the data Korean now describe
it by width. Mutating the folded heading to measure characters instead of
display columns still fails "the folded heading spends what the type and
status leave on the name, in display columns".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Euisan Kim <git@san.kim>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
euisan added a commit to euisan/teamclaude that referenced this pull request Oct 1, 2026
* feat(tui): fold account rows below 70 columns so usage bars wrap

On a narrow terminal (a phone over a remote session, say) an account row
could not hold its name, status and quota bars on one line. The list dropped
every bar past the first and shrank that one to a cell or two, so the weekly
and family windows vanished below 70 columns and the session bar became
unreadable near 40.

Below LIST_MIN a full-width list now folds each account into a heading
(marker, route cells, name, type, status) and one line per quota bar, with
the trailing tags wrapped a whole tag at a time beneath them. Each folded
bar takes the row less a 2-column right margin (COMPACT_BAR_RIGHT_MARGIN),
so it never touches the screen edge. The heading gives the name every
column the rest leaves, measured in display columns, so a CJK name is cut
where it reaches the type column. The smallest drawable terminal drops from
40x8 to 30x8 so the folded layout is reachable at phone widths.

This ports teamcodex KarpelesLab#21 (7296e40, compact layout below 70 columns) and
KarpelesLab#23 (0a34280, compact bar right margin), adapted to teamclaude's budgeted
_listLayout: the layout flags `narrow`, _renderRow passes the width, and
_renderAcct returns lines instead of one string. Split panes are never
narrow. teamcodex KarpelesLab#12/KarpelesLab#17/KarpelesLab#18/KarpelesLab#19 are already in 1.1.22 and were not needed.

At 70 columns and wider the frame is byte-identical to 1.1.22: the trailing
tags moved into a list but are appended in the same order with the same
separator. test/fixtures/tui-wide-frames.json holds frames recorded from the
unmodified renderer, and test/tui-narrow-bars.test.js pins both sides.

docs/teamcodex-port-plan.md classifies all 34 teamcodex commits for later
porting; docs/evidence/ holds the real TUI.start() paint at 30-80 columns.

* test(tui): use a neutral double-width name in the narrow-layout fixtures

The width tests need an account name of East Asian Wide characters, but the
one they used read like a real person's name and address. It is now
'가나다라마바사-wide@example.test': seven Hangul syllables (fourteen columns)
then ASCII, which still exercises the cases the tests care about.

- At 40 columns the name is cut exactly where it reaches the type column.
- At 30 columns a fifth syllable would straddle the edge, so it is dropped
  whole and its leftover column padded (a case the old name did not hit).
- At 69 columns the whole name fits.

The wide-frame fixture was re-recorded from the unmodified 1.1.22 renderer
with the new name. The pty capture in docs/evidence was regenerated from the
real TUI.start() paint. The comments that named the data Korean now describe
it by width. Mutating the folded heading to measure characters instead of
display columns still fails "the folded heading spends what the type and
status leave on the name, in display columns".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
euisan added a commit to euisan/teamclaude that referenced this pull request Oct 5, 2026
* feat(tui): fold account rows below 70 columns so usage bars wrap

On a narrow terminal (a phone over a remote session, say) an account row
could not hold its name, status and quota bars on one line. The list dropped
every bar past the first and shrank that one to a cell or two, so the weekly
and family windows vanished below 70 columns and the session bar became
unreadable near 40.

Below LIST_MIN a full-width list now folds each account into a heading
(marker, route cells, name, type, status) and one line per quota bar, with
the trailing tags wrapped a whole tag at a time beneath them. Each folded
bar takes the row less a 2-column right margin (COMPACT_BAR_RIGHT_MARGIN),
so it never touches the screen edge. The heading gives the name every
column the rest leaves, measured in display columns, so a CJK name is cut
where it reaches the type column. The smallest drawable terminal drops from
40x8 to 30x8 so the folded layout is reachable at phone widths.

This ports teamcodex KarpelesLab#21 (7296e40, compact layout below 70 columns) and
KarpelesLab#23 (0a34280, compact bar right margin), adapted to teamclaude's budgeted
_listLayout: the layout flags `narrow`, _renderRow passes the width, and
_renderAcct returns lines instead of one string. Split panes are never
narrow. teamcodex KarpelesLab#12/KarpelesLab#17/KarpelesLab#18/KarpelesLab#19 are already in 1.1.22 and were not needed.

At 70 columns and wider the frame is byte-identical to 1.1.22: the trailing
tags moved into a list but are appended in the same order with the same
separator. test/fixtures/tui-wide-frames.json holds frames recorded from the
unmodified renderer, and test/tui-narrow-bars.test.js pins both sides.

docs/teamcodex-port-plan.md classifies all 34 teamcodex commits for later
porting; docs/evidence/ holds the real TUI.start() paint at 30-80 columns.

* test(tui): use a neutral double-width name in the narrow-layout fixtures

The width tests need an account name of East Asian Wide characters, but the
one they used read like a real person's name and address. It is now
'가나다라마바사-wide@example.test': seven Hangul syllables (fourteen columns)
then ASCII, which still exercises the cases the tests care about.

- At 40 columns the name is cut exactly where it reaches the type column.
- At 30 columns a fifth syllable would straddle the edge, so it is dropped
  whole and its leftover column padded (a case the old name did not hit).
- At 69 columns the whole name fits.

The wide-frame fixture was re-recorded from the unmodified 1.1.22 renderer
with the new name. The pty capture in docs/evidence was regenerated from the
real TUI.start() paint. The comments that named the data Korean now describe
it by width. Mutating the folded heading to measure characters instead of
display columns still fails "the folded heading spends what the type and
status leave on the name, in display columns".

Reapplied on 1.1.23: tui-wide-frames.json re-recorded from the unmodified 1.1.23 renderer (percentage off by default since KarpelesLab#514); renderFrame takes a config, and the folded-label test checks percentage on and off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

3 participants