Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

@ammar-agent ammar-agent commented Oct 12, 2025

Workspaces now sort by most recent user message. Makes context-switching more intuitive—recently active work surfaces first.

Implementation:

  • Tracks last user message timestamp (not assistant) to avoid reordering during concurrent streams
  • Computed synchronously in ProjectSidebar memo (no layout shift on first render)
  • Uses persisted message timestamps (survives restarts, correct during replay)

Architecture:

  • No separate hook needed - timestamps come directly from WorkspaceState
  • Simpler than initial approach (net -47 LoC vs original branch start)
  • Single source of truth: message metadata

Generated with cmux

Workspaces within each project now sort by most recent stream activity first.
This surfaces recently active work and makes context-switching more intuitive.

Architecture improvements:
- New useWorkspaceRecency hook tracks last stream start per workspace
- Timestamps persist in localStorage (cross-tab sync enabled)
- Memoized sorting prevents unnecessary recomputation
- Clean separation: recency tracking isolated from UI concerns

Implementation:
- Stream start detection via workspace state transitions
- Sort comparator: most recent timestamp first, never-streamed workspaces last
- Fallback to config order when metadata unavailable

Net impact: +54 LoC (28 in new hook, +26 in integration)

Generated with `cmux`
Generated with `cmux`
Generated with `cmux`
When a workspace is deleted, automatically remove its timestamp from localStorage.
Prevents stale data from affecting sort order after deletions.

Uses functional setState to check for stale IDs within the update,
avoiding extra effect dependencies that would cause unnecessary re-runs.

Generated with `cmux`
Fixes robustness issues with app restarts and history replay:
- Timestamps now come from assistant message metadata (persisted in chat history)
- Survives app restarts without reset
- Correct during history replay (uses original message timestamp, not replay time)
- Simplified: no longer tracks streaming transitions, just syncs from message state

Implementation:
- WorkspaceState.lastStreamStart pulled from most recent assistant message
- useWorkspaceRecency syncs timestamps from workspace states
- Single source of truth: message metadata timestamps

Generated with `cmux`
- Change from assistant messages to user messages (avoids reordering during concurrent streams)
- Remove useWorkspaceRecency hook - compute timestamps directly in ProjectSidebar memo
- Fixes layout shift on first load (timestamps now computed synchronously)
- Rename lastStreamStart → lastUserMessageAt (clearer semantics)
- Net -47 LoC (removed entire hook, simplified architecture)

Generated with `cmux`
- Extract workspaceRecency as memoized Record<string, number> in useWorkspaceAggregators
- Only updates when timestamps actually change (stable reference optimization)
- Prevents unnecessary sort recomputation in ProjectSidebar when unrelated state changes
- Added deep comparison to avoid identity changes when values unchanged

Performance impact:
- Before: New Map on every forceUpdate() → memo recomputes on every stream event
- After: Stable reference unless timestamps change → memo only recomputes when needed

Generated with `cmux`
@ammario ammario enabled auto-merge October 12, 2025 20:36
@ammario ammario added this pull request to the merge queue Oct 12, 2025
Merged via the queue into main with commit 50cc53b Oct 12, 2025
7 checks passed
@ammario ammario deleted the workspace-recency-sort branch October 12, 2025 20:47
ammar-agent added a commit that referenced this pull request Oct 13, 2025
Navigation now follows visual order displayed in sidebar.

**Problem:**
- PR #205 added recency-based sorting to workspace display
- Ctrl+J/K navigation still used unsorted config order
- Caused confusion: pressing next didn't select next visible workspace

**Solution:**
- Move sorting logic from ProjectSidebar to App.tsx
- Share sorted list between navigation and display
- Both now use same recency-sorted order

**Changes:**
- Added sortedWorkspacesByProject memo in App.tsx
- Updated handleNavigateWorkspace to use sorted list
- Pass sorted list through LeftSidebar to ProjectSidebar
- Remove duplicate sorting logic from ProjectSidebar
ammario pushed a commit that referenced this pull request Oct 13, 2025
Workspace navigation (Ctrl+J/K) now follows the visual order displayed
in the sidebar.

## Problem

PR #205 added recency-based sorting to the workspace display, but
Ctrl+J/K navigation still used the unsorted config order. This caused
confusion where pressing "next workspace" wouldn't select the next
visible workspace in the sidebar.

## Solution

Moved the sorting logic from ProjectSidebar to App.tsx so both
navigation and display use the same recency-sorted workspace list. This
ensures Ctrl+J/K navigation matches the visual order users see.

## Changes

- Added `sortedWorkspacesByProject` memo in App.tsx that sorts
workspaces by recency
- Updated `handleNavigateWorkspace` to use the sorted list instead of
raw config order
- Pass sorted list through LeftSidebar → ProjectSidebar props chain
- Removed duplicate sorting logic from ProjectSidebar (now uses parent's
sorted list)

## Testing

- ✅ All 379 unit tests pass
- ✅ Type checking passes
- Manual verification: Ctrl+J/K now navigates in the same order as
displayed in sidebar

_Generated with `cmux`_
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