Commit Graph

44 Commits

Author SHA1 Message Date
Shantur Rathore
f77fb1562e fix(ui): stabilize streaming follow mode
Disable follow-mode virtualization churn and simplify reasoning header layout so streaming thinking blocks stop nudging the scroll position while the list is pinned to bottom.
2026-03-10 18:44:55 +00:00
Shantur Rathore
c64a9a03f9 fix(ui): stabilize virtual message list measurements 2026-03-10 06:28:11 +00:00
Shantur Rathore
bec1af6523 fix(ui): keep delete selection consistent across stream and timeline 2026-03-04 00:41:23 +00:00
Shantur Rathore
3719dcecf8 fix(ui): clear timeline selection on stream click 2026-03-03 23:00:44 +00:00
Shantur Rathore
3dae143830 Merge origin/dev into dev 2026-03-03 22:57:43 +00:00
Shantur Rathore
f050273a8e fix(ui): preserve stream scroll on session switch 2026-03-03 22:44:18 +00:00
Shantur Rathore
4f8aba5658 chore(ui): tighten and center bulk delete toolbar 2026-03-03 18:52:09 +00:00
Shantur Rathore
17716a730b chore(ui): use Kbd hints in bulk delete toolbar 2026-03-03 18:28:00 +00:00
Shantur Rathore
80a02b68b9 fix(ui): restrict selection and xray to post-compaction 2026-03-03 14:09:48 +00:00
VooDisss
ec0bffe0c2 fix(ui): enable tool-part delete and long-press group toggle 2026-03-03 09:26:17 +02:00
VooDisss
ed322a16bf chore(ui): finalize timeline selection audit fixes
Complete re-review of PR #188 (commits 224cab6 feature + 2c27fc5 perf/i18n follow-up). Gatekeeper focus: standards, correctness, perf/complexity, and translation completeness.

What this changes (pre -> post)

Pre: timeline primarily navigation/hover preview; bulk delete selection message-level and token metrics tied to backend assistant output tokens (missing tool payload weight).

Post: segment-level timeline selection + range (Shift) + toggle (Ctrl/Meta) + mobile long-press; histogram ribs overlay showing relative + absolute (~10k cap) token weight; assistant-turn grouping to avoid adjacency bugs; bulk-delete toolbar shows Before / Selection / After token pills.

Code standards / correctness

OK: Solid signal/memo/effect patterns with cleanup; no obvious lifecycle leaks. Grouping avoids adjacency overlap by mapping messageId to turns.

Fix: selection-id stability is mitigated by pruning stale ids after segment rebuilds; long term stable ids from part ids/toolPartIds remain recommended.

Fix: token counts now share getPartCharCount in both x-ray overlay and bulk-delete toolbar, keeping estimates consistent with live store updates.

Performance / complexity

OK: O(n^2) hotspots removed for liveSegmentChars and selectedTokenTotal. groupRole + deleteUpTo hover checks now memoize messageId sets/maps.

Note: getPartCharCount can be heavy for large tool payloads but remains gated behind selection mode.

CSS / UI integration

Fix: x-ray token label now uses theme tokens instead of hard-coded colors. Delete toolbar now uses menu-based controls with selection-mode toggle.

i18n

Fix: selection hint now renders Cmd/Ctrl via localized modifier placeholder; all locales updated.
2026-03-03 03:49:51 +02:00
Shantur Rathore
b6bf58ea8f fix(ui): keep stream virtualized and bottom-anchored while loading 2026-03-02 22:47:21 +00:00
VooDisss
2c27fc53ad perf(ui): fix O(n²) in liveSegmentChars and selectedTokenTotal, add i18n + SSR guard
Addresses bot review feedback on commit 224cab6.

## Performance: liveSegmentChars O(n²) → O(n)

The memo had three inner loops scanning all props.segments per unique
messageId. Added a single O(n) pre-pass building a
segmentsByMessageId Map, then replaced all three inner loops with
map lookups. Total complexity: O(n) instead of O(m×n).

File: packages/ui/src/components/message-timeline.tsx

## Performance: selectedTokenTotal O(n²) → O(n)

For each selected messageId, the memo scanned all segments to sum
chars. On "Select all" this was O(selected × segments). Now builds a
charsByMessageId map in one O(n) pass and does O(1) lookups per
selected message. Same pattern as aggregateTokensByMessageId.

File: packages/ui/src/components/message-section.tsx

## SSR guard: resize listener

window.addEventListener("resize", computeBadgeLayout) lacked a
typeof window !== "undefined" guard. Other window usage in the file
was guarded. Wrapped the addEventListener, requestAnimationFrame, and
onCleanup block in the guard.

File: packages/ui/src/components/message-timeline.tsx

## i18n: mirror selectionHint key in 5 locale files

messageSection.bulkDelete.selectionHint was only defined in
en/messaging.ts. Added the key (English string, since Ctrl/Shift/Esc
are universal keyboard labels) to es, fr, ja, ru, and zh-Hans.

Files: packages/ui/src/lib/i18n/messages/{es,fr,ja,ru,zh-Hans}/messaging.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:46:51 +02:00
VooDisss
224cab6a42 feat(ui): add timeline segment selection, xray token histogram, and group logic overhaul
Overhauls the message timeline sidebar with segment-level selection,
token-aware xray histogram bars, and messageId-based grouping — replacing
the previous message-level selection and positional adjacency logic.

## Selection System (SELECTION-SYSTEM)

- Dual-level selection: `selectedTimelineIds` (segment IDs) as the
  source of truth, bridged to `selectedForDeletion` (message IDs) via
  a reactive `createEffect`.
- CTRL+Click: toggles individual segments. Clicking an assistant parent
  with unexpanded tools expands the group and selects all members.
  Re-clicking collapses and deselects.
- SHIFT+Click: range selection. Direction follows anchor state — if the
  anchor is selected the range is additive; if not, subtractive.
- Escape: clears all selection via a global keydown listener.
- Long-press (500ms, 10px jitter tolerance): mobile/touch selection
  via pointer events with context-menu suppression.
- Scroll anchor preservation: captures badge offsetTop before toggling
  visibility, restores scrollTop after layout shift.

## Token Count Fix (TOKEN-COUNT-FIX)

- New `getPartCharCount()` estimates characters for any `ClientPart`.
  Handles text, tool state (input/output/metadata), and content arrays.
- **Skips `filediff` metadata key** — this key contains full before/after
  file content that inflated character counts by 10-100x.
- `totalChars` field added to `TimelineSegment` and `PendingSegment`,
  accumulated during `buildTimelineSegments()`.

## Scroll Performance (SCROLL-PERF)

- Two-tier positioning replaces per-badge `getBoundingClientRect` on
  every scroll event:
  1. `computeBadgeLayout()` — expensive pass, runs once on activation,
     resize, or expansion. Stores `layoutTop` relative to scroll content.
  2. `handleScrollRaf()` — RAF-throttled, reads 1 container rect per
     frame. Derives all badge screen positions arithmetically.
- `clipBounds` subtracts delete toolbar height + 16px gap when toolbar
  is visible, preventing xray bars from overlapping the toolbar.

## Group Logic (GROUP-LOGIC)

- `getAdjacentGroup()`: changed from backward positional walk to
  `segments.filter(s => s.messageId === clicked.messageId)`. Fixes
  cross-message group overlap when consecutive tool segments belong to
  different assistant messages.
- `groupRole()`: checks for sibling tools via `messageId`.
- `isGroupStart()`: checks previous segment's `messageId`.
- Only assistant badges trigger group selection; tool and user badges
  are always standalone.

## Active Highlight (ACTIVE-HIGHLIGHT)

- Renamed `activeMessageId` → `activeSegmentId` (signal, prop, and
  comparison). Clicking a badge now highlights only that specific badge,
  not all badges sharing the same messageId.
- Intersection observer resolves messageId → first segment's id.
- Auto-scroll effect uses segment id directly (no `.find()` lookup).

## XRay Histogram Bars (XRAY-BARS)

- Portal-based overlay with two bars per segment:
  - Relative bar: width = tokens/maxTokens, green-to-red gradient.
  - Absolute bar: width = tokens/10000 (capped), grey, with red glow
    overflow indicator when tokens exceed ABSOLUTE_TOKEN_CAP (10K).
- Token labels as pill-shaped badges (white bg, dark border, 12px font,
  1.5rem height matching badge height) at the left tip of each bar.
- `liveSegmentChars` memo fetches fresh char counts from the message
  store to handle stale tool output that arrived after segment creation.
- `aggregateTokensByMessageId` memo: O(n) pre-computation replacing the
  previous O(n²) per-segment iteration inside `<For>`.
- `clip-path: inset(...)` clips bars at layout edges.

## Delete Toolbar Token Display (TOKEN-TOTAL-IN-TOOLBAR)

- Removed `outputTokensByMessageId` (backend `entry.outputTokens` only
  counted assistant output, missing tool result content entirely).
- `selectedTokenTotal` now sums `seg.totalChars` across all segments
  for each selected messageId, divides by 4. Consistent with xray bars.
- Three color-coded pills: Before (muted, current context), Selection
  (red, tokens being removed), After (green, remaining after deletion).
  Eliminates mental arithmetic for users targeting a context token count.

## Delete Hover Fix

- Removed `selected.has(segment.messageId)` → `return true` from
  `isDeleteHovered()`. The red delete overlay now only activates from
  actual hover interactions (kind === "message" or "deleteUpTo"), not
  from the selection state. This prevents the red overlay from masking
  the blue segment-level selection highlight.

## CSS Changes

- message-selection.css: Restyled toolbar with accent-primary scheme,
  three-pill token group, button variants (--delete, --cancel), hint.
- message-timeline.css: Selection styling (!important overrides), group
  indicators (left border), xray overlay (fixed fullscreen, z-index 40),
  rib/bar/label styles, container layout, stacking context isolation.

## Files Changed

- packages/ui/src/components/message-section.tsx (+345/-197)
- packages/ui/src/components/message-timeline.tsx (+671/-199)
- packages/ui/src/lib/i18n/messages/en/messaging.ts (+1/-2)
- packages/ui/src/styles/messaging/message-selection.css (+107/-34)
- packages/ui/src/styles/messaging/message-timeline.css (+146/-0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 09:51:59 +02:00
Shantur Rathore
48b2d7c5ee refactor(ui): extract virtual-follow-list for message stream 2026-03-01 20:14:21 +00:00
Shantur Rathore
594809538d Revert "perf(ui): start streams at newest"
This reverts commit 13802537b4.
2026-03-01 12:41:22 +00:00
Shantur Rathore
13802537b4 perf(ui): start streams at newest
Reverse the message stream scroll layout so the viewport naturally starts at the newest messages and keeps older content virtualized. Use sentinel-based edge chasing to make jump-to-top/bottom land reliably despite VirtualItem mounts.
2026-03-01 12:40:18 +00:00
Shantur Rathore
ca2b3c232f perf(ui): drop virtualized DOM in hidden panes
Add DOM instrumentation tags and harden VirtualItem visibility for hidden/zero-sized roots to prevent inactive instances from keeping heavy tool-call markup mounted; restore message stream virtualization margin.
2026-02-28 14:13:42 +00:00
Shantur Rathore
ab9e188b02 feat(ui): add multi-select message deletion 2026-02-26 15:25:47 +00:00
Shantur Rathore
2991de528a feat(ui): add delete-up-to action and range hover overlay 2026-02-26 13:46:48 +00:00
Shantur Rathore
b91dbb1a60 fix(ui): sync delete-hover overlays across preview and stream 2026-02-26 10:10:46 +00:00
Shantur Rathore
0f9c99e3bd feat(ui): mirror delete hover overlay in timeline 2026-02-25 23:32:32 +00:00
Shantur Rathore
c7195469bd fix(ui): add keyboard shortcut hints toggle
Hide shortcut hints in WebUI and allow toggling in native desktop apps.
2026-02-14 00:02:56 +00:00
Shantur Rathore
56a0e8aa6e fix(ui): refresh timeline when parts change
Track per-message part count changes and rebuild timeline segments so deletions or streaming updates don't leave stale entries in the message timeline.
2026-02-08 21:32:35 +00:00
Shantur Rathore
88605a4617 feat(ui): add copy option for selected text 2026-02-05 23:20:13 +00:00
Shantur Rathore
5b1e21345f feat(ui): localize UI strings
Converts hardcoded UI copy to i18n keys across the app, adds global translation for non-component modules, and splits the English catalog into feature modules with duplicate-key detection.
2026-01-26 12:26:12 +00:00
Shantur Rathore
4ed2361387 Reduce scroll jitter from virtual items 2025-12-14 15:55:09 +00:00
Shantur Rathore
557c2500c7 Clean up legacy instance shell and theme additions 2025-12-14 01:55:50 +00:00
Shantur Rathore
766be00ded Make message list bottom-first with append-only timeline 2025-12-12 12:00:19 +00:00
Shantur Rathore
c323667729 Preserve session scroll when returning 2025-12-09 21:29:48 +00:00
Shantur Rathore
783fb5c5b2 Defer initial message scroll until list renders 2025-12-09 17:09:30 +00:00
Shantur Rathore
82ff1916b7 Prevent cached session remeasurements and remove logs 2025-12-09 16:27:01 +00:00
Shantur Rathore
8204143810 Improve session cache eviction 2025-12-09 16:27:01 +00:00
Shantur Rathore
7aba3c1221 add timeline tool visibility toggle 2025-12-08 18:32:23 +00:00
Shantur Rathore
67ff00d83e feat: add quote mode options 2025-12-08 10:45:12 +00:00
Shantur Rathore
dc702b1fb2 feat: quote message selections 2025-12-08 10:16:58 +00:00
Shantur Rathore
9b0e02f66f feat: add timeline hover message preview 2025-12-08 09:45:11 +00:00
Shantur Rathore
a2e5034c20 feat: sync timeline highlight with scroll 2025-12-07 21:59:57 +00:00
Shantur Rathore
e3489b22e6 feat: add timeline sidebar to message view 2025-12-07 21:40:52 +00:00
Shantur Rathore
3373e23a41 sync hidden sidebar layout with mobile 2025-12-06 22:34:30 +00:00
Shantur Rathore
2514fa94b4 stabilize scroll event wiring 2025-12-05 22:05:27 +00:00
Shantur Rathore
522910ff64 refine message stream auto scroll 2025-12-05 21:57:10 +00:00
Shantur Rathore
78338f33c1 add responsive session sidebar 2025-12-02 23:52:45 +00:00
Shantur Rathore
f9ec757c64 refactor message stream layout 2025-12-02 19:23:05 +00:00