Skip to content

fix(ui): UX pass — defaults, sizing, labels, layout, per-message JSON#8

Merged
0bserver07 merged 4 commits into
mainfrom
fix/ux-pass-2
Apr 25, 2026
Merged

fix(ui): UX pass — defaults, sizing, labels, layout, per-message JSON#8
0bserver07 merged 4 commits into
mainfrom
fix/ux-pass-2

Conversation

@0bserver07
Copy link
Copy Markdown
Owner

Six small UX fixes from your post-v0.3.4 review.

# Fix Files
1 Default page size 25 → 10 on cost-tab tables CommandCostList · OutlierCommandsTable · RetryAlertsPanel
2 Token Composition donut bigger (height 260→360, radii 60/95→85/135) TokenCompositionDonut
3 Top Sessions y-axis: `<short_id> · ` instead of bare hash SessionCostBarChart
4 Overview Date Range mini-card: locale-formatted (`Jan 30, 2026`) instead of ISO slice OverviewTab
5 Overview layout: CacheRoiCard + TokenCompositionDonut share a 2-col grid; donut no longer stretches full-width OverviewTab
6 Per-message `JSON` toggle in session viewer messages — independent of the global Raw JSON / Formatted switch SessionsTab `ConversationMessage`

Verified live: chimera Date Range now reads "Feb 23, 2026 → to Apr 25, 2026". Bundle hash: `index-cpZMlWb2.js`. typecheck + build clean.

🤖 Generated with Claude Code

0bserver07 and others added 4 commits April 25, 2026 15:47
…, date range, layout, per-message JSON

- Default page size on cost-tab tables 25 → 10 (CommandCostList,
  OutlierCommandsTable, RetryAlertsPanel).
- TokenCompositionDonut: height 260 → 360, inner radius 60 → 85, outer
  95 → 135. Reads as a hero card instead of a small chart in a big box.
- SessionCostBarChart: y-axis label is now `<short_id> · <first prompt
  preview>` so each bar is recognisable. Width bumped 80 → 320.
- OverviewTab Date Range mini-card: ISO slice (`01-30T20:58:11.193Z`)
  replaced with locale `Jan 30, 2026`.
- OverviewTab layout: CacheRoiCard + TokenCompositionDonut now share a
  2-col grid on lg+ instead of stacking full-width — donut no longer
  stretches to a full page band.
- ConversationMessage in SessionsTab: per-message `JSON` toggle in the
  header. Independent from the global "Raw JSON" / "Formatted" switch
  so users can drill into one message without flipping the whole view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…eights

- CommandsTab: prompt cell switches from `truncate` (single-line ellipsis)
  to `whitespace-pre-wrap break-words`. Slice limit bumped 200 → 400 chars
  before the ellipsis cap so a typical prompt fits in 3-4 wrapped lines.
- MessagesTab: same treatment, slice limit 150 → 300.
- All Overview charts now render at height={280}: TokenUsage, DailyCost,
  ToolUsage, ModelDistribution, HourlyPattern, CommandToolDist,
  ErrorDistribution (were 250). ErrorCategory / ErrorRate / Interruption
  were already 280.
- ToolUsageBarChart was unbounded (`Math.max(250, n*32)` → 700+ on busy
  projects, dwarfed its row sibling). Capped at 420.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two bugs that left full-text search empty for any project ingested
after the pipeline → stats rename in 0.3.0.

1. `SearchService.reindex_all` imported `from ..pipeline import process`
   — module no longer exists. Hitting "Reindex" returned
   `cannot import name 'process' from 'stackunderflow.pipeline'`.
   Replaced with `queries.get_project_stats(conn, project_id=...)`,
   which already runs the modern dedup → classify → enrich → aggregate
   chain and returns the same `(messages, stats)` shape.

2. The schema has UNIQUE(provider, slug) so a project used through both
   Claude and Codex has two rows with the same slug. `index_project`
   does a DELETE-by-slug before inserting; iterating rows naively let
   the second iteration wipe the first's messages (chimera ended up
   with 0 indexed rows). Group rows by slug and concatenate messages
   before calling `index_project` once per slug.

Memory_cache / cache_service kwargs are now unused — kept on the
signature so existing callers still type-check.

Verified live: post-reindex, `/api/search?q=refactor&project=-Users-...-chimera`
returns 74 hits (was 0). Total index: 216,364 messages across 147 slugs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…) + 0.3.5

Three services had `from ..pipeline import process` left over from the
0.3.0 rename — search, tags, qa. Hitting Reindex on any of those tabs
silently failed. Each `reindex_all` now uses
`queries.get_project_stats(conn, project_id=...)` from the store, and
groups rows by slug before indexing so duplicate (claude + codex) slugs
don't wipe each other's data.

Bump to 0.3.5; CHANGELOG entry covers the search fix and the UX pass
that's been accumulating on this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@0bserver07 0bserver07 merged commit fee6800 into main Apr 25, 2026
7 of 8 checks passed
@0bserver07 0bserver07 deleted the fix/ux-pass-2 branch April 25, 2026 20:32
0bserver07 added a commit that referenced this pull request May 14, 2026
0bserver07 added a commit that referenced this pull request May 14, 2026
0bserver07 added a commit that referenced this pull request May 20, 2026
…#8)

* fix(ui): UX pass — page-size defaults, donut size, session bar labels, date range, layout, per-message JSON

- Default page size on cost-tab tables 25 → 10 (CommandCostList,
  OutlierCommandsTable, RetryAlertsPanel).
- TokenCompositionDonut: height 260 → 360, inner radius 60 → 85, outer
  95 → 135. Reads as a hero card instead of a small chart in a big box.
- SessionCostBarChart: y-axis label is now `<short_id> · <first prompt
  preview>` so each bar is recognisable. Width bumped 80 → 320.
- OverviewTab Date Range mini-card: ISO slice (`01-30T20:58:11.193Z`)
  replaced with locale `Jan 30, 2026`.
- OverviewTab layout: CacheRoiCard + TokenCompositionDonut now share a
  2-col grid on lg+ instead of stacking full-width — donut no longer
  stretches to a full page band.
- ConversationMessage in SessionsTab: per-message `JSON` toggle in the
  header. Independent from the global "Raw JSON" / "Formatted" switch
  so users can drill into one message without flipping the whole view.

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

* fix(ui): wrap Commands/Messages prompts; standardize Overview chart heights

- CommandsTab: prompt cell switches from `truncate` (single-line ellipsis)
  to `whitespace-pre-wrap break-words`. Slice limit bumped 200 → 400 chars
  before the ellipsis cap so a typical prompt fits in 3-4 wrapped lines.
- MessagesTab: same treatment, slice limit 150 → 300.
- All Overview charts now render at height={280}: TokenUsage, DailyCost,
  ToolUsage, ModelDistribution, HourlyPattern, CommandToolDist,
  ErrorDistribution (were 250). ErrorCategory / ErrorRate / Interruption
  were already 280.
- ToolUsageBarChart was unbounded (`Math.max(250, n*32)` → 700+ on busy
  projects, dwarfed its row sibling). Capped at 420.

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

* fix(search): repair reindex + dedupe duplicate slugs

Two bugs that left full-text search empty for any project ingested
after the pipeline → stats rename in 0.3.0.

1. `SearchService.reindex_all` imported `from ..pipeline import process`
   — module no longer exists. Hitting "Reindex" returned
   `cannot import name 'process' from 'stackunderflow.pipeline'`.
   Replaced with `queries.get_project_stats(conn, project_id=...)`,
   which already runs the modern dedup → classify → enrich → aggregate
   chain and returns the same `(messages, stats)` shape.

2. The schema has UNIQUE(provider, slug) so a project used through both
   Claude and Codex has two rows with the same slug. `index_project`
   does a DELETE-by-slug before inserting; iterating rows naively let
   the second iteration wipe the first's messages (chimera ended up
   with 0 indexed rows). Group rows by slug and concatenate messages
   before calling `index_project` once per slug.

Memory_cache / cache_service kwargs are now unused — kept on the
signature so existing callers still type-check.

Verified live: post-reindex, `/api/search?q=refactor&project=-Users-...-chimera`
returns 74 hits (was 0). Total index: 216,364 messages across 147 slugs.

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

* fix: repair tag + qa reindexes (same broken pipeline import as search) + 0.3.5

Three services had `from ..pipeline import process` left over from the
0.3.0 rename — search, tags, qa. Hitting Reindex on any of those tabs
silently failed. Each `reindex_all` now uses
`queries.get_project_stats(conn, project_id=...)` from the store, and
groups rows by slug before indexing so duplicate (claude + codex) slugs
don't wipe each other's data.

Bump to 0.3.5; CHANGELOG entry covers the search fix and the UX pass
that's been accumulating on this branch.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0bserver07 added a commit that referenced this pull request May 20, 2026
Two ``stackunderflow start`` invocations against the same store would
otherwise both spawn filesystem watchers, racing on ingest + mart
refresh. New ``stackunderflow/etl/lock.py`` fences the watcher behind
an OS-level non-blocking advisory lock (fcntl on POSIX, msvcrt on
Windows). When the lock is held by another live instance, this process
serves HTTP normally but skips the watcher spawn.

Closes HANDOFF §"What's left" item #8.

Surfaces:
- ``--no-lock`` CLI flag + ``STACKUNDERFLOW_DISABLE_LOCK=1`` env to opt
  out for tests / headless setups.
- ``GET /api/etl/status`` ``watcher.lock_held_by`` field (PID or null).
- ``stackunderflow etl status`` CLI surfaces the same.

Stale-PID handling: if the file records a PID that's no longer alive,
acquire reclaims cleanly. Released on FastAPI lifespan shutdown +
atexit fallback for abnormal exits.

Tests: 16 new in tests/stackunderflow/etl/test_lock.py. Existing
``test_etl_status::test_empty_store_returns_complete_shape`` updated
for the new ``lock_held_by`` key. Total: 1614 passed (1598 baseline +
16 new), 2 skipped, 11 deselected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
0bserver07 added a commit that referenced this pull request May 20, 2026
…oint in time

Closes HANDOFF follow-up #8 (backend half).

Adds GET /api/playback/{session_id}/fs?at=<iso>&paths=<csv>&include_content=…
returning the per-file state reconstructed by replaying the session's Read /
Write / Edit / MultiEdit / NotebookEdit calls up to ``at``. Pure read-side
over messages.raw_json — no schema migration.

Response shape: {session_id, snapshot_ts, files: {path: {content?, byte_count,
last_modified_ts, operations_applied, reconstruction_complete}}, warnings}.
404 on unknown session, 422 on unparseable ``at``, 200 with files={} for
a session with no FS-touching calls before ``at``.

- Service: stackunderflow/services/playback_fs.py
  Read seeds initial content (strips Claude Code's "     N<tab>" line-number
  prefix so subsequent Edits match raw file bytes). Write replaces full
  content. Edit/MultiEdit substitute old_string → new_string; an Edit without
  prior Read records new_string as best-effort content and marks
  reconstruction_complete=False. An Edit with non-matching old_string is
  skipped with a "substitution skipped" warning; prior content preserved.
  MultiEdit applies each sub-edit in order with per-edit miss warnings.
  NotebookEdit accumulates {cell_id: source} JSON (never marked complete —
  we only see touched cells, not the full .ipynb tree). replace_all is
  honoured on both Edit and per-sub-edit MultiEdit.

- Route: stackunderflow/routes/playback.py — registered *before* the
  /api/playback/{session_id} v1 route so FastAPI matches /fs first.

- Tests: 20 service cases + 9 route cases. Full suite: 2384 passed, 2
  skipped (was 2355).

- Frontend type contract: PlaybackFsFileEntry + PlaybackFsSnapshotResponse
  in stackunderflow-ui/src/types/api.ts for the dashboard agent.

Perf on real-shape store, session with 6906 assistant msgs, 455 files,
1.7MB reconstructed text (Read/Write/Edit mix 577/412/729): ~150ms median.
With paths=[busiest_file] filter or earlier cutoff: 60-70ms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0bserver07 added a commit that referenced this pull request May 20, 2026
0bserver07 added a commit that referenced this pull request May 20, 2026
…OFF #8)

Adds the Playback v2 file-browser side panel on top of the v1 event-stream
view. As the user scrubs through tool calls, the panel reconstructs the
state of every touched file at the current scrubber timestamp by calling
the v2 backend route `GET /api/playback/{session}/fs?at=<iso>`.

- `PlaybackFsPanel.tsx` — collapsible panel with a directory-grouped file
  tree (left) + a monospace content viewer with line numbers (right).
  Warnings banner when the snapshot reports partial reconstructions.
  Empty state when `files: {}`. Reuses `@tabler/icons-react` + Tailwind,
  no new npm deps.
- `playbackFs.ts` — pure helpers (`humanizeBytes`, `formatSnapshotTs`,
  `groupFilesByDirectory`, `debounce`). Separated from the component so
  the test suite (Node's built-in runner, no DOM) can lock the formatting
  + tree-building logic.
- `getPlaybackFsSnapshot()` added to `services/api.ts`. Handles 200 / 404
  (generic Error) / 422 (`PlaybackFsBadTimestampError` sentinel).
  Forwards `at`, optional `paths`, `include_content`.
- 250ms debounce inside the panel's scrub effect. An in-flight
  `sessionId|at` request key guards against stale responses overwriting
  fresh ones during a rapid keyboard scrub (j/k mash).
- Bandwidth optimisation: while no file is selected, the scrub fetches
  with `include_content=false` (metadata-only). On file selection, a
  second targeted fetch with `paths=[that_file]&include_content=true`
  pulls just that body.
- 25 new tests in `tests/services/playback-fs.test.ts` — suite count
  110 -> 135. API client (200 / 404 / 422 / with-content / without /
  paths / URL-encoding / warnings), helpers (byte edges / ts edges /
  dir grouping / debounce semantics), panel composition seams
  (file-selection / empty-state / warnings contracts).

Closes HANDOFF follow-up #8 (frontend half — backend shipped earlier
this release at commit 8beff84). Frontend tests (135) + typecheck +
build all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0bserver07 added a commit that referenced this pull request May 20, 2026
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.

1 participant