Skip to content

Improve session History on mobile and add message minimap - #55

Merged
powerfooI merged 10 commits into
mainfrom
improve-mobile-session-history
Aug 26, 2026
Merged

Improve session History on mobile and add message minimap#55
powerfooI merged 10 commits into
mainfrom
improve-mobile-session-history

Conversation

@powerfooI

@powerfooI powerfooI commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Summary

Improves the agent session History view on phones and adds a message minimap for navigating long histories.

Changes:

  • Add a message minimap above the History timeline: one color-coded bar per message (blue for user, green for assistant). Bars for the messages currently in the viewport grow as a moving wave while scrolling (compositor-only transform: scaleY, so it stays smooth on mobile), the strip auto-scrolls to keep the wave visible in long histories with a slim position indicator (the heavy global overlay scrollbar is excluded there), and tapping or dragging anywhere on the strip scrubs the timeline to the matching position with a brief card highlight.
  • Always show user and assistant messages together; drop the assistant-message filter switch.
  • Move the Turns/Tokens overview strip into the Details tab on all viewports, so the Messages tab keeps the space for the timeline; on phones also hide the Inspector's Files/Changes/History tab bar (the bottom navigation already switches views) and shrink the full-message preview font.
  • Render the full-message and transcript dialogs at the document root via portal so they stay within the visible viewport and above the top bar when the on-screen keyboard or pinch zoom shrinks the app.
  • Only open focus-triggered tooltips for keyboard-style (:focus-visible) focus, so tapping shortcut buttons on touch devices no longer leaves a tooltip stuck.
  • Keep the History drawer's bottom buttons above the phone's safe area so they remain tappable instead of sliding under the browser's bottom bar or home indicator.

Verification

  • bun run format:check && bun run lint && bun run test — 715 tests pass.
  • cd web && bun run typecheck && bun run build; cd server && bun run typecheck.
  • Manually verified on a phone over LAN: the minimap wave follows scrolling and bars settle back when messages leave the viewport, tap/drag on the strip scrubs the timeline, the long-message dialog stays within the viewport and closes, tooltips dismiss after taps, and the footer buttons are tappable.

Copilot AI lite review requested due to automatic review settings August 26, 2026 14:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are confirmed runtime/performance issues in the new minimap/tooltip logic (optional-chaining bug and potential selector exception, plus an avoidable O(n) per-frame scroll cost) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This pull request improves the mobile session History experience in the web/ React UI by adding a scroll-synced message minimap for long timelines, simplifying message visibility (user+assistant always together), and tightening mobile layout/overlay behavior so dialogs and controls remain usable with mobile viewport quirks.

Changes:

  • Add a message minimap to the History drawer (scroll wave + tap/drag scrubbing + card highlight) and remove the assistant-only filter.
  • Render message/transcript dialogs via document.body portals and adjust modal sizing/safe-area handling for mobile.
  • Restrict focus-triggered tooltips to keyboard-style focus (:focus-visible) to avoid “stuck” tooltips after touch taps.
File summaries
File Description
web/src/styles.css Adds minimap styling, mobile layout refinements, modal sizing tweaks, reduced-motion handling, and safe-area padding.
web/src/components/GlobalTooltip.tsx Gates focus-triggered tooltips behind :focus-visible to prevent touch-tap tooltip stickiness.
web/src/components/AgentSessionPreviewDialog.tsx Ports the transcript dialog to document.body to avoid mobile stacking/viewport issues.
web/src/components/agentSession.ts Removes message-filtering helper no longer needed after dropping the assistant filter switch.
web/src/components/agentSession.test.ts Removes tests for the deleted filtering helper.
web/src/components/AgentMessageDialog.tsx Ports the full-message dialog to document.body for consistent mobile overlay behavior.
web/src/components/AgentHistoryDrawer.tsx Implements the minimap, scroll/viewport tracking, highlight behavior, and removes assistant filtering UI/state.
CHANGELOG.md Documents the mobile History improvements, minimap, tooltip behavior change, and safe-area fixes.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread web/src/components/AgentHistoryDrawer.tsx
Comment thread web/src/components/AgentHistoryDrawer.tsx Outdated
Comment thread web/src/components/GlobalTooltip.tsx Outdated
Copilot AI review requested due to automatic review settings August 26, 2026 14:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The minimap currently triggers duplicate selection on tap/click (pointer-down scrubbing plus per-bar click), causing redundant scroll/highlight behavior.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

web/src/components/AgentHistoryDrawer.tsx:280

  • Clicking/tapping a minimap bar currently triggers onSelect twice: once via the strip's onPointerDown (bubbled from the button) and again via the bar button's onClick. This can restart smooth scrolling and reset the highlight timer unnecessarily. Consider removing the per-bar onClick and handling keyboard activation explicitly, since pointer interactions are already covered by the strip-level pointer handlers.
            }
            title={`#${sequence} ${roleLabel}`}
            aria-label={`Go to message ${sequence} (${roleLabel})`}
            onClick={() => onSelect(sequence)}
          />
  • Files reviewed: 8/8 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings August 26, 2026 14:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The minimap bars currently trigger selection twice on pointer interactions (parent onPointerDown + child onClick), which can cause redundant smooth-scroll/highlight behavior and should be corrected before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

web/src/components/AgentHistoryDrawer.tsx:308

  • Each minimap bar is a with its own onClick, but the parent .agent-history-minimap also calls onSelect() in onPointerDown. A pointer tap/click on a bar will therefore invoke onSelect twice (once on pointerdown, once on click), which can restart scrollIntoView({behavior:"smooth"}) and reset the highlight timer unnecessarily.
              }
              title={`#${sequence} ${roleLabel}`}
              aria-label={`Go to message ${sequence} (${roleLabel})`}
              onClick={() => onSelect(sequence)}
            />
  • Files reviewed: 9/9 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread web/src/components/AgentHistoryDrawer.tsx Outdated
Copilot AI review requested due to automatic review settings August 26, 2026 14:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are a couple of confirmed runtime/behavior issues in the new minimap implementation (pointer selection double-fire and unguarded ResizeObserver usage) that should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

Previously missed (2) — in code that hasn't changed since the last review.

web/src/components/AgentHistoryDrawer.tsx:280

  • Tapping a minimap bar can trigger onSelect twice: once via the strip’s onPointerDown (which calls onSelect) and again via the bar button’s onClick. This can cause redundant scroll/highlight work and can make scrubbing feel glitchy. Consider only selecting on pointer-down when the event originated from the strip itself (padding/gap), and let bar buttons handle taps/clicks.
  const handlePointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {
    // Capture so a drag keeps scrubbing even off the strip.
    event.currentTarget.setPointerCapture(event.pointerId);
    scrubbingRef.current = true;
    const sequence = sequenceAtClientX(event.clientX);
    if (sequence !== null) onSelect(sequence);
  };

web/src/components/AgentHistoryDrawer.tsx:317

  • ResizeObserver is used here without a feature check. In environments where it’s unavailable, this will throw at effect setup time and break the minimap (the repo already guards ResizeObserver in contextMenuPosition.ts). Consider guarding it and using optional chaining in cleanup.

This issue also appears on line 616 of the same file.

    const resizeObserver = new ResizeObserver(update);
    resizeObserver.observe(strip);
    strip.addEventListener("scroll", update, { passive: true });
    return () => {
      resizeObserver.disconnect();
      strip.removeEventListener("scroll", update);
    };

web/src/components/AgentHistoryDrawer.tsx:631

  • The visible-range tracking effect also constructs a ResizeObserver without checking for support. If ResizeObserver is undefined, this effect will throw and the drawer won’t render. Guarding it (as done elsewhere in the repo) avoids that hard failure.
    const resizeObserver = new ResizeObserver(schedule);
    resizeObserver.observe(root);
    // Scroll events do not bubble, but capture listeners on window fire for
    // scrolls on any descendant, so this covers the content container and
    // any ancestor that ends up scrolling instead.
    window.addEventListener("scroll", schedule, {
      passive: true,
      capture: true,
    });
    window.addEventListener("resize", schedule);
    return () => {
      if (frame !== 0) window.cancelAnimationFrame(frame);
      resizeObserver.disconnect();
      window.removeEventListener("scroll", schedule, true);
      window.removeEventListener("resize", schedule);
    };
  • Files reviewed: 9/9 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread web/src/components/AgentHistoryDrawer.tsx Outdated
Copilot AI review requested due to automatic review settings August 26, 2026 14:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

There are a couple of small but concrete correctness/maintainability issues in the new minimap implementation (debug flag behavior vs comment, and hard-coded gap math for scrubbing) that should be addressed before approval.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

web/src/components/AgentHistoryDrawer.tsx:81

  • MINIMAP_DEBUG_ENABLED is documented as being enabled with ?debug-minimap=1, but the current .has("debug-minimap") check will also enable it for ?debug-minimap=0 (or any value). Either update the comment or make the check match the documented =1 behavior so the flag is unambiguous.
// Temporary diagnostics for the minimap wave, enabled with ?debug-minimap=1.
const MINIMAP_DEBUG_ENABLED =
  typeof window !== "undefined" &&
  new URLSearchParams(window.location.search).has("debug-minimap");

web/src/components/AgentHistoryDrawer.tsx:258

  • sequenceAtClientX hard-codes the minimap bar gap (+ 2) to match the CSS gap: 2px. This will silently break scrubbing math if the gap changes (e.g. responsive tweaks). Derive the gap from computed styles so the mapping stays correct when the CSS changes.
      // Keep the gap in sync with .agent-history-minimap in styles.css.
      const stride = firstBar.offsetWidth + 2;
      if (stride <= 0) return null;
  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings August 26, 2026 14:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new minimap viewport tracking uses ResizeObserver without a guard, which can crash in environments where ResizeObserver is unavailable.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

web/src/components/AgentHistoryDrawer.tsx:578

  • ResizeObserver is used unguarded here; on platforms/environments where it’s undefined, constructing it will throw and break the History drawer. Consider the same defensive check used elsewhere (e.g. create it only when available) and null-check in cleanup.
    const resizeObserver = new ResizeObserver(schedule);
    resizeObserver.observe(root);
  • Files reviewed: 9/9 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings August 26, 2026 15:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Comment thread web/src/components/AgentHistoryDrawer.tsx Outdated
Comment thread web/src/components/AgentHistoryDrawer.tsx Outdated
Comment thread web/src/components/AgentHistoryDrawer.tsx Outdated
Comment thread web/src/components/AgentHistoryDrawer.tsx
Copilot AI review requested due to automatic review settings August 26, 2026 15:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Comment thread web/src/components/AgentHistoryDrawer.tsx
Comment thread web/src/components/AgentHistoryDrawer.tsx
Comment thread web/src/components/AgentHistoryDrawer.tsx
Copilot AI review requested due to automatic review settings August 26, 2026 15:41
@powerfooI
powerfooI merged commit 5bea623 into main Aug 26, 2026
1 check passed
@powerfooI
powerfooI deleted the improve-mobile-session-history branch August 26, 2026 16:11
@powerfooI
powerfooI removed the request for review from Copilot August 26, 2026 17:12
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.

2 participants