fix(ui): UX pass — defaults, sizing, labels, layout, per-message JSON#8
Merged
Conversation
…, 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
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
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six small UX fixes from your post-v0.3.4 review.
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