Skip to content

feat(app): add project grouping, filtering, and pinned sessions to sidebar inbox - #59220

Closed
wycocoyu wants to merge 1 commit into
anomalyco:devfrom
wycocoyu:sidebar-inbox-projects
Closed

wycocoyu wants to merge 1 commit into
anomalyco:devfrom
wycocoyu:sidebar-inbox-projects

Conversation

@wycocoyu

Copy link
Copy Markdown

Issue for this PR

N/A (feature enhancement for the experimental SidebarInbox mode)

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Other:

What does this PR do?

When working across multiple repositories and worktrees, the SidebarInbox (Recent list) mixes sessions from every project into a single flat stream and drops older reference sessions once since moves forward.

This PR adds three lightweight navigation controls to SidebarInbox (packages/app/src/shell/titlebar/inbox.tsx and packages/app/src/shell/titlebar/inbox-model.ts):

  1. Pinned sessions (Pinned): Sessions can be pinned or unpinned from their context menu (Menu.Context). Pinned sessions stay visible in a Pinned section above Recent even if their last idle timestamp precedes since, while still moving to Needs attention or Working whenever they become active.
  2. Project filter: When sessions span multiple projects (or a filter/grouping option is active), a compact header menu at the top of SidebarInbox lets you scope the inbox to a single project (aggregating worktrees under their parent project root) or switch back to All projects. If the filtered project no longer has any sessions, the view falls back to All projects automatically instead of stranding the sidebar on an empty filter.
  3. Group by project: A Group by project toggle in the same menu groups Recent sessions into collapsible per-project sections ordered by most recent activity, keeping the currently open session visible even when its project group is collapsed (matching the Working shelf behavior).

How did you verify your code works?

  • Added unit tests in packages/app/src/shell/titlebar/inbox-model.test.ts covering partitionInboxEntries (pinned ordering, since persistence, busy/archived section precedence, and project filter fallback), inboxProjects (worktree aggregation and duplicate project name disambiguation), and groupInboxByProject (recency-preserving project grouping):
    • cd packages/app && bun test --conditions=solid --isolate --preload ../ui/test/solid-jsx.ts --preload ./happydom.ts src/shell/titlebar/inbox-model.test.ts --rerun-each 10 (40 pass, 0 fail)
  • Ran TypeScript compiler check:
    • cd packages/app && bun run typecheck:tsc (0 errors)
  • Ran changed-file linter:
    • bun script/lint-changed.ts origin/dev (0 problem(s) in 0 of 4 changed files)

Visual

<style> .label { font: 500 8px ui-sans-serif, system-ui, sans-serif; fill: currentColor; } .muted { opacity: 0.45; } .box { fill: none; stroke: currentColor; stroke-width: 1; opacity: 0.35; rx: 4; } .row { fill: currentColor; opacity: 0.14; rx: 2; } .pin { fill: currentColor; opacity: 0.75; rx: 2; } .flow { stroke: currentColor; stroke-width: 1.2; fill: none; stroke-dasharray: 3 3; animation: dash 2.4s linear infinite; opacity: 0.6; } .pulse { animation: fade 3.2s ease-in-out infinite; } @Keyframes dash { to { stroke-dashoffset: -12; } } @Keyframes fade { 0%, 100% { opacity: 0.25; } 50% { opacity: 0.9; } } </style> Flat Recent Pinned Project A

Checklist

  • I have read the CONTRIBUTING.md file and followed the guidelines.
  • This PR was not Vibe Coded, and I have thorough understanding of every single line of code I have written.

@opencode-agent opencode-agent 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.

The model logic looks right and the change is limited to the experimental Inbox sidebar. The new tests pass and tsc -b is clean. I couldn't run the app here (the change is in packages/app, and I can only drive the TUI), so could you add before and after screenshots or a short video of the Pinned section, the project menu and grouped Recent?

Public API: unchanged. The only new persisted fields are optional additions to the local InboxState (pinned, project, groupByProject, collapsedProjects), so existing saved state still loads.

Concerns, most important first (details inline):

  • With a project filter active, sessions from other projects that need attention or are working are hidden too, so it's easy to miss an approval.
  • Pinned and collapsed keys are never cleaned up.
  • The InboxRow status markup rewrite isn't needed for this feature; please drop it to keep the diff focused.

const project =
options.project && entries.some((entry) => inboxProjectKey(entry) === options.project) ? options.project : undefined

const scoped = project ? entries.filter((entry) => inboxProjectKey(entry) === project) : entries

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 filter applies to needs and working as well as Recent. With a project selected, a session in another project that is waiting for approval or input disappears from the sidebar completely, which defeats the point of a "Needs attention" shelf. Could the filter apply only to Recent, Pinned and Archived? Or at least keep needs (and probably working) unscoped, or show how many are hidden outside the filter.


const pinnedSet = createMemo(() => new Set(state.pinned ?? []))

const togglePin = (key: string) => {

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.

pinned (and collapsedProjects below) only ever lose a key when the user toggles it back. Keys for deleted sessions, sessions forgotten after a not-found error, or projects that no longer exist stay in persisted storage forever. Please remove a pin when its session is deleted or forgotten (e.g. in the remove and forget paths), or prune both lists against the loaded entries once they have loaded.

const project = createMemo(() => props.entry.ctx.projects.forSession(props.entry.session))
const status = () => props.entry.status

const nonReadyStatus = (): Exclude<InboxStatus, "ready"> | undefined => {

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 status rendering refactor (nonReadyStatus plus the re-indented <Show> block) isn't part of pinning or grouping and doesn't change behaviour. Please leave it out so the diff stays about the feature.

@zhuzhutai

Copy link
Copy Markdown

+1 to this direction — grouped Recent with a project filter is what I want from the desktop sidebar.

Context: Windows desktop build, two repos (hqt_flutter_t2, app_bridge_net), dozens of sessions.
The flat Recent stream is the main reason I keep another editor open just to see "what was I doing in
project X". I'm happy to test this branch on Windows x64 and post before/after screenshots if that
helps unblock review — I'm not claiming a code review here, just user-side requirements:

  • Group headers should key on server + full worktree path, so same-basename checkouts and worktrees
    don't merge into one section.
  • Keep Needs attention / Working unscoped, or show a "N hidden outside this project" hint.
    Losing an approval prompt because a filter is active is worse than a mixed list (matches the bot's
    first concern).
  • Persist groupByProject / collapsedProjects per server, and prune both pinned and
    collapsedProjects when a session or project goes away (bot's second concern).
  • The currently open session should stay visible when its project group is collapsed.

+1 on dropping the InboxRow status markup rewrite if it isn't needed for this feature — a smaller
diff is easier to land.

@rahbi4-source

Copy link
Copy Markdown

Hi! I tested this branch on Windows (built from source, bun run dev:desktop, then Settings → Preferences → Interface → Inbox) with my own sessions across several projects, and it works as described:

"Group by project" gives collapsible per-project sections in Recent; the collapsed state and the toggle survive app restarts.
The project filter scopes the list as expected, and worktrees of the same repo aggregate under one group.
Pin/unpin from the row context menu works; pinned sessions stay in Pinned across restarts and past the recency cutoff.
Grouping by server + full path is exactly the right key for me: I have same-named checkouts on different paths and they correctly stay in separate groups.

From a daily-driver perspective I'd second the review points: with a project filter active it is easy to miss sessions in other projects that are waiting for approval, so keeping Needs attention / Working unscoped (or a hint like "N hidden outside this project") would make this safe to leave on all the time. Happy to re-test on Windows whenever you push an update.

Also, this delivers most of what #55446 asks for, in Inbox mode — linking the issue might bring the right eyeballs here.

Thanks for working on this!

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.

3 participants