Skip to content

feat(status-bar): consolidate memory + sessions into Resource Usage segment#1415

Open
Jinwoo-H wants to merge 10 commits intomainfrom
Jinwoo-H/consolidated-resource-usage
Open

feat(status-bar): consolidate memory + sessions into Resource Usage segment#1415
Jinwoo-H wants to merge 10 commits intomainfrom
Jinwoo-H/consolidated-resource-usage

Conversation

@Jinwoo-H
Copy link
Copy Markdown
Contributor

@Jinwoo-H Jinwoo-H commented May 5, 2026

Summary

Merges the Memory and Terminal Sessions status-bar segments into a single Resource Usage segment. The popover has a Resources / Terminal Sessions tab strip, daemon Restart daemon and Kill all sessions buttons in the footer, and a Daemon not responding banner when both data sources are unreachable.

The new segment is gated by a single `resource-usage` `StatusBarItem` that defaults ON; persisted `memory` / `sessions` values migrate in place on hydration so users who had either enabled keep the segment visible.

Daemon restart + kill-all flows live in a new shared `useDaemonActions` hook so the settings pane (`ManageSessionsSection`) and the status bar stay in sync — no copy-paste.

Direction decisions (Phase 0.5 design review)

  1. Button label: "Kill all sessions" (matches `pty.management.killAll()` semantics; no new `shutdown` IPC).
  2. Tab structure: 2 tabs (Resources, Terminal Sessions) — CPU and Memory share the same worktree-tree data, so they live in one tab with click-to-sort columns.
  3. Always on: single `resource-usage` toggle, default ON. Users can still hide via context menu or Appearance settings.
  4. Shared hook: `useDaemonActions` extracted before the second consumer was wired.

Files

  • `src/shared/types.ts`, `src/shared/constants.ts` — `StatusBarItem` union + defaults.
  • `src/renderer/src/store/slices/ui.ts` — `migrateStatusBarItems` rewrites legacy `memory`/`sessions` → `resource-usage` without stripping unknown ids (forward-compat on downgrade).
  • `src/renderer/src/store/slices/memory.ts` — new `memorySnapshotError` so the banner can fire instead of silently swallowing errors.
  • `src/renderer/src/components/shared/useDaemonActions.tsx` — new hook + `DaemonActionDialog` with anti-dismiss guards and lifecycle callbacks (so `ManageSessionsSection` keeps its optimistic kill-all rollback).
  • `src/renderer/src/components/settings/ManageSessionsSection.tsx` — restart + kill-all routed through the hook; kill-one unchanged.
  • `src/renderer/src/components/status-bar/ResourceUsageStatusSegment.tsx` — new consolidated segment.
  • `src/renderer/src/components/status-bar/StatusBar.tsx`, `AppearancePane.tsx` — toggles + render point at the new single item.
  • Deleted `MemoryStatusSegment.tsx` and `SessionsStatusSegment.tsx`.

Verification

  • `pnpm typecheck` clean.
  • `pnpm lint` (oxlint) — 0 warnings, 0 errors across 1279 files.
  • `pnpm test` — all 8 `ui` slice tests + status-bar tests pass; full suite has 13 pre-existing failures unrelated to this PR (`better-sqlite3 NODE_MODULE_VERSION` mismatch in main-process tests).
  • Codex review (gpt-5.5, medium reasoning) iterations: P1 → P2 → no actionable issues.

Out of scope

  • Adding a real `pty:management:shutdown` IPC.
  • Persisting last-selected tab across restarts.
  • Keyboard shortcuts for the popover.

Made with Orca 🐋

Jinwoo-H and others added 10 commits May 4, 2026 20:04
…egment

Merges the Memory and Terminal Sessions status-bar segments into a single
Resource Usage segment with a Resources / Terminal Sessions tab strip,
daemon Restart and Kill-all-sessions actions in the popover footer, and a
'Daemon not responding' banner when both subsystems are unreachable.

The new segment is gated by a single 'resource-usage' StatusBarItem that
defaults ON; legacy 'memory' and 'sessions' values migrate in place on
load so users who had either enabled keep the new segment visible.

Daemon restart + kill-all dialog flows are extracted into a shared
useDaemonActions hook so the settings pane and status bar stay in sync.

Co-authored-by: Orca <help@stably.ai>
…ions

Replaces the heavyweight Radix Tabs strip with a compact segmented pill
switcher in the popover header, and moves the Restart daemon and Kill
all sessions buttons to icon-only triggers in the top-right of the same
header. Removes the full-width footer and the unused Tabs primitive
import. Tooltips carry the verbs so the icons stay scannable.

Co-authored-by: Orca <help@stably.ai>
Wraps the Resources/Sessions tab content in a fixed-height container so
switching between the two panes doesn't reflow the popover. The Sessions
panel now fills its parent (h-full + min-h-0) and owns its own scroll
instead of capping at a max-h, so empty/short states leave whitespace
instead of collapsing the surface.

Co-authored-by: Orca <help@stably.ai>
Mirrors the Settings > Manage Sessions row behavior. Every session now
gets a kill X in the trailing slot; bound sessions hide it until the
row is hovered/focused, orphan sessions show it always. Clicking opens
a confirm Dialog (anti-dismiss while in-flight) that calls pty.kill on
confirm — same flow the settings pane uses.

Co-authored-by: Orca <help@stably.ai>
…d list

The popover used to have two tabs: Resources (per-worktree CPU/Mem from
the local memory collector, which excludes SSH PTYs by design) and
Sessions (every PTY the daemon tracks, local + SSH). With SSH worktrees
in use, Sessions could show many worktrees while Resources only showed
one — a confusing inconsistency between two adjacent tabs.

This consolidates them into a single grouped list (repo → worktree →
session) sourced from the union of MemorySnapshot.worktrees and
pty.listSessions(). Local rows render numeric CPU/Mem; remote rows
render '—' and carry a '· remote' badge on the worktree, with the
parent repo also flagged when it contains remote children. Kill-X on
every session row with confirm Dialog (mirrors Settings > Manage
Sessions). Optimistic removal so killed rows disappear immediately
rather than lingering up to the 10s sessions-poll. Focus is restored
to the popover body after the dialog closes.

The merge is renderer-only and pure: a new mergeSnapshotAndSessions
module emits a renderer-local view-model (cpu/memory: number | null) so
shared types are untouched. SSH-only sessions resolve to their worktree
group via the @@-parse fallback when no tab in this renderer claims the
PTY. 12 unit tests cover the dedup, @@-parse, tab-walk-wins, repo
aggregate, and all 4 interaction-state paths.

Removes the tab switcher and the SessionsTabPanel component. Net -128
lines in the segment file.

Co-authored-by: Orca <help@stably.ai>
…sibility

Two parity regressions surfaced by a feature-parity audit against the
pre-merge two-tab popover:

1. Worktree row was disabled-for-navigation whenever the renderer
   couldn't find a sidebar storeRecord. That suppressed click for every
   synthesized SSH worktree row, even though navigateToWorktree already
   safely handles the unknown case via activateAndRevealWorktree's own
   no-op path. Split the gate into two flags: isNavigable (only blocks
   the synthetic Unattributed/Orphan buckets) and showWorktreeActions
   (still requires a storeRecord, so Sleep/Delete keep working).

2. The sessions-only IPC error path was invisible after the merge — the
   old SessionsTabPanel rendered "Terminal sessions unavailable". The
   merged popover only showed the full daemon-unreachable banner when
   BOTH IPCs failed. Add a slim inline notice for the partial-failure
   case so users understand why the session list looks empty/stale even
   though the resource numbers are fine.

Co-authored-by: Orca <help@stably.ai>
Two compounding perf issues were making the whole app feel laggy:

1. ResourceUsageStatusSegment is always mounted in the status bar and
   subscribed to runtimePaneTitlesByTabId via useAppStore. That object
   mutates on every keystroke in any open terminal pane, so every
   keystroke was re-running mergeSnapshotAndSessions across every
   worktree and session in the workspace, even though the popover was
   closed and the result was never displayed. Gate the merge on the
   open flag so it only runs when the popover is actually showing.

2. The merge itself did O(N) linear scans over ptyIdsByTabId and
   tabsByWorktree for every session it processed (S * (T + W) work).
   With many sessions and tabs that adds up. Pre-build O(1) lookup
   indices once per merge instead. The bound set is now derived from
   the same index instead of doing a separate Object.values flat walk.

Also removed a duplicate boundPtyIds memo that was paying the same
flat-walk cost just to compute orphanCount; folded that into a single
cheap pass that runs only on the inputs orphanCount actually depends
on.

Co-authored-by: Orca <help@stably.ai>
Reserves a fixed-width trailing gutter on every row (session, worktree,
repo, Orca app) AND on the column header. The kill-X button now lives
inside the gutter on session rows; other rows leave it empty. CPU and
Memory cells stay aligned with their column headers regardless of
whether a row carries an action button.

Also renames the bulk-kill button copy from "Kill N Orphan(s)" to
"Kill N orphan terminal(s)" to match the surrounding label
conventions and clarify what's being killed.

Co-authored-by: Orca <help@stably.ai>
Orphan sessions have no tab in this Orca instance, so there's no
"unsaved work in that pane" risk that justifies the confirm step.
Clicking the X on an orphan now optimistically removes the row and
fires pty.kill straight away — same UX as the bulk Kill orphan
terminals button. Bound sessions still confirm because killing them
takes a tab the user is actively working in.

Co-authored-by: Orca <help@stably.ai>
navigateToWorktree and navigateToTab no longer close the popover, so
users can chain multiple jumps (browse worktrees, hop between
terminals) without re-opening the segment each time. The popover
still closes via outside-click / Escape.

Co-authored-by: Orca <help@stably.ai>
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.

1 participant