Skip to content

feat(app): unslop sessions tab with view modes, collapsible groups, and running indicator - #60374

Closed
tomlin7 wants to merge 2 commits into
anomalyco:devfrom
tomlin7:sessions-tab-unslop
Closed

tomlin7 wants to merge 2 commits into
anomalyco:devfrom
tomlin7:sessions-tab-unslop

Conversation

@tomlin7

@tomlin7 tomlin7 commented Oct 29, 2026 •

Copy link
Copy Markdown

Summary

Refactored the sessions tab to improve organization and visual feedback:

Features

  • View modes: Toggle between date-based grouping (Today, Yesterday, Older) and project-based grouping
  • Collapsible project groups: Click chevron to expand/collapse, shows session count
  • Always-visible paths: Worktree name and branch displayed for all sessions (not just on hover)
  • Running indicator: Small pulsing dot on session avatar for active sessions

Implementation

  • Added \HomeSessionViewMode\ store with date/project modes
  • Added \groupSessionsByProject()\ function for project-based grouping
  • Extended \HomeSessionStatusController\ to expose
    unning\ state
  • Added CSS animation for running indicator with reduced-motion support
  • All user-facing strings use i18n keys (added \home.sessions.viewMode.date\ and \home.sessions.viewMode.project)

Files Changed

  • \packages/app/src/home/sessions/controller.tsx\ - View mode state, grouping logic
  • \packages/app/src/home/sessions/view.tsx\ - Toggle, collapsible headers, running indicator
  • \packages/app/src/home/sessions/view.css\ - Pulse animation
  • \packages/app/src/home/sessions/region.tsx\ - Prop wiring
  • \packages/app/src/runtime/i18n/en.ts\ - i18n keys

Added by the opencode bot (AI-generated).

Related: #55446

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Oct 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 72 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@opencodejr opencodejr Bot 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.

This doesn't work as submitted yet. bun run typecheck in packages/app fails on the new code: createStore and displayName are used in controller.tsx without being imported, so Home would throw as soon as the sessions controller is created. The project groups also don't match the types that createHomeScrollController (route.tsx:19) and the titleOpacity/setHeader props in region.tsx expect. The scroll and sticky-header code needs to handle the new group shape, not just have its types widened.

Public API: unchanged (app UI only).

Other concerns, most important first (details inline):

  • The group body in view.tsx reads group(), viewMode and the collapsed state once, so collapsing, switching view mode and live session updates stop working, in the date view too.
  • HomeSessionLocation now always shows the location, including on mobile, and the hover and Alt-hold reveal no longer does anything.
  • The running dot shows the same state as the avatar's existing busy indicator.
  • This PR also contains the prompt navigation rail commit (prompt-rail.*, virtualizer.tsx, the timeline e2e spec and two i18n keys), which is unrelated and is the same as your open #57869. Please drop it from this PR so each change can be reviewed on its own. Note that dev recently removed the turn navigator (ee8221c).

Once it compiles, before/after screenshots of Home in both view modes, with a group collapsed, would help.

Related: #55446

const platform = usePlatform()
const queryClient = useQueryClient()

const [viewMode, setViewMode] = createStore<{ mode: HomeSessionViewMode }>({ mode: "date" })

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.

createStore isn't imported (only solid-js is), and neither is displayName at line 461. Type-checking fails, and this throws at runtime as soon as Home builds the sessions controller. record.projectName already holds the resolved display name, so you could use that instead of displayName.

].filter((group) => group.sessions.length > 0)
}

function groupSessionsByProject(records: HomeSessionRecord[]): HomeSessionProjectGroup[] {

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.

groups can now return HomeSessionProjectGroup[], but createHomeScrollController in home/scroll.ts (used from route.tsx) is typed and keyed on the today/yesterday/older ids for its header offsets and title fades. Widening the view's prop types to string doesn't change that, so region.tsx fails type-checking. Giving project groups the same { id, title, sessions } shape as date groups would let the scroll code and the view handle both without casts.

</>
)}
{(group, index) => {
const isProjectGroup = props.viewMode === "project"

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.

Index runs this callback once per slot, without tracking, so isProjectGroup, groupTitle, groupSessions and isCollapsed are captured on first render and never update. Clicking the chevron won't collapse the group, switching mode won't refresh the slots that stay mounted, and <Key each={groupSessions}> keeps the first array it got, so new or moved sessions stop showing up in the date view too. Read these inside the JSX, or as accessors such as () => props.isGroupCollapsed(group().id).

desktop: boolean
}) {
if (!props.desktop) return null
const worktree = () => props.location.worktree

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.

This drops the desktop guard and the show/instantHide handling, so worktree and branch now show on every row, mobile included, and squeeze titles there. The Alt-hold and hover reveal (showLocations, show=) is still wired up but no longer does anything. If always-visible locations is the goal, the leftover reveal code should go too, and narrow layouts still need handling.

unread: avatar.unread,
loading: avatar.loading,
open: () => props.isOpenTab(props.record),
running: () => avatar.loading() && !avatar.unread(),

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.

avatar.loading() is already false whenever the session needs attention (which is what makes unread true), so running is effectively the same as loading, which the avatar already shows as busy. The dot repeats an existing indicator rather than adding information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:compliance This means the issue will auto-close after 2 hours.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant