Skip to content

feat: per-session run commands + sidebar action-icon cleanup#34

Merged
AThraen merged 22 commits into
mainfrom
feat/run-commands
May 12, 2026
Merged

feat: per-session run commands + sidebar action-icon cleanup#34
AThraen merged 22 commits into
mainfrom
feat/run-commands

Conversation

@AThraen
Copy link
Copy Markdown
Contributor

@AThraen AThraen commented May 12, 2026

Summary

Two related additions to the per-session UI:

  • Per-session run commands (issue Add play button to run the session's main project #33). Each session keeps a list of labelled commands (Test / Build / Watch / Run …). A toolbar runs the default; the chevron exposes the rest; F5 / Shift+F5 run/stop the default. Runs execute in a separate headless PseudoTerminal wrapped in a Windows Job Object so the whole child tree dies when the session sleeps or closes — a Claude session is untouched. Output streams into a chips-and-drawer UI; the drawer offers Stop, Copy, and Send to terminal (which wraps in a fenced preamble for Claude parents and falls back to clipboard for non-Claude shells). New sessions are seeded from RunCommandTemplatesService based on top-level project detection (dotnet → cargo → node → python → make). Editor dialog reachable from right-click, the dropdown's "Edit commands…", and the sidebar right-click "Session commands ▸" submenu.

  • Sidebar action-icon cleanup (partially addresses issue UI cleanup: rework per-session action icons and reduce overall button clutter #29). The sidebar row button stack drops from 6 (📁 >_ ➕ ✏ 💤 ✕) to 3 (➕ 💤 ✕), governed by a new SidebarActionIconsMode setting: OnHover (default) / Always / Hidden. OnHover keeps the panel laid out — no text reflow when hovering — but transparent and non-interactive until the row is hovered. Open in Explorer / Open PowerShell here / Rename move to the right-click context menu so nothing is lost (rename uses the same in-place editor as double-click). The terminal toolbar gains its own close button so it's a complete canonical home for per-session actions.

Also redesigns the Run Commands editor dialog: the clipped "Default" column header is now a centered ★ glyph, columns get explicit widths with min-widths, row padding is more comfortable, and + Add command / Cancel / Save share one footer row.

Related issues

  • Closes Add play button to run the session's main project #33 — Add play button to run the session's main project. Implementation diverges from the original proposal in two ways: runs go to a separate headless PTY instead of the active terminal (a Claude session shouldn't be polluted by dotnet run output), and a session carries a list of labelled commands rather than a single RunCommand: string?. Both were validated by issue comments and confirmed during implementation.
  • Partially addresses UI cleanup: rework per-session action icons and reduce overall button clutter #29 — UI cleanup of per-session action icons. The icon-density problem on sidebar rows is fixed via reduction + hover-reveal + a configurable mode, and the terminal toolbar is now a self-sufficient action surface. Deferred to a separate change: reorganizing the Settings window into tabs / advanced toggle, and auditing the main toolbar.

Design notes

  • Job Object lifetime. PseudoTerminal gained useJobObject: true plus JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so child trees (npm test, dotnet watch, …) die with the wrapping PTY. RunInstance always opts in; the parent session PTY does not.
  • Output capture. RunInstance strips ANSI and buffers up to 1 MB per run. Not persisted — runs are ephemeral and killed on session close / sleep / app exit.
  • Single-default invariant. RunCommandItem.EnsureSingleDefault(list) is called on save and on seed; the dialog uses a RadioButton group so the invariant survives editing.
  • Sleep teardown. SleepSession explicitly calls vm.Runner.StopAll() before the VM disposes so child processes die before the WebView2 / xterm controls start tearing down.
  • Hover panel layout. OnHover mode flips Opacity + IsHitTestVisible but keeps Visibility=Visible so the row never reflows when the cursor enters or leaves. Hidden mode collapses the panel and reclaims the horizontal space for the text.
  • Live setting propagation. A new _sidebarActionPanels dictionary lets SettingsButton_Click flip every row to the new mode immediately — no sidebar rebuild needed. _sidebarRenameActions captures the per-row in-place rename closure so the context menu Rename entry runs the same code path as a double-click.

Test plan

Recommend running with --clean for an isolated state:

dotnet run --project src/CodeShellManager/CodeShellManager.csproj -- --clean

Run commands (#33)

  • Toolbar ▶ runs the default. Create a session in a .NET project. ▶ appears in the toolbar; clicking it spawns a headless run and a blue chip appears in the chips strip above the terminal. Chip turns green on success, pink on failure.
  • Chevron ▼ dropdown. Configure 2+ commands via the editor; ▼ lists them with the default starred. Selecting a non-default runs it without changing the default.
  • F5 / Shift+F5. F5 runs the default of the active session; Shift+F5 stops it. Doesn't fire when typing into the terminal (xterm captures it first via the existing accelerator surfacing).
  • Drawer. Click a chip — drawer opens with output + ⏹ Stop / 📋 Copy / ↗ Send to terminal. Copy puts the captured output on the clipboard. Send-to-terminal on a Claude parent wraps the command in a fenced preamble and writes to the PTY (no auto-Enter); on a non-Claude shell it falls back to clipboard + toast.
  • Project-type seed. Create a fresh session in a node project (top-level package.json); the new RunCommands list pre-populates with sensible templates (npm install / test / start, plus framework-aware extras). Same for dotnet / cargo / python / make. SSH sessions skip seeding.
  • Editor dialog. Right-click ▶ → editor opens with rows for each command. Reorder via ▲ ▼, set default via the radio column, +Add / 🗑 work. Save persists across restart. The ★ header is centered and not clipped.
  • Sidebar submenu. Right-click a session → Session commands ▸ lists each command with the default labelled (default); clicking runs it. Edit commands… opens the dialog.
  • Cleanup on sleep / close / exit. Start a long-running command (ping -t 8.8.8.8); sleep the session — the chip clears, no orphan ping.exe survives in Task Manager. Same for close. Same for app exit while a run is in flight.

Sidebar action-icon cleanup (#29)

  • OnHover default. Fresh install: sidebar rows show no inline action icons. Hovering a row fades in ➕ 💤 ✕ on the right (with no text reflow — the right column is reserved). Cursor leaves → icons fade back to invisible. Active and selected styling is unaffected.
  • Always mode. Settings → Appearance → "Sidebar action icons" → "Always show". Icons persist regardless of hover.
  • Hidden mode. Same dropdown → "Hide". Icons disappear; the session text expands into the reclaimed space. Right-click still exposes every action (Rename, Open in Explorer, Open PowerShell here, Duplicate session, New session here…, sleep, close, etc.).
  • Live propagation. Change the mode in Settings → click Save → existing sidebar rows update immediately without needing a session re-create or app restart.
  • Rename via right-click. Right-click any session → Rename session. The in-place editor opens on the row (same path as double-click); Enter saves, Escape cancels.
  • Terminal toolbar ✕. A new ✕ at the far right of every terminal toolbar — clicking closes that session. Sits to the right of the existing ▶ ▼ 💤 📝 >_ 📁 cluster and the status dot.

Run Commands dialog redesign

  • No clipping. Open the dialog with 2+ commands. The first column header is a centered ★ (not the truncated word "Default"). Tooltip on the star explains it's the default-row marker.
  • Resize behavior. Drag the resize grip — columns honor their min-widths; the dialog clamps at the new MinWidth=560, MinHeight=360.
  • Footer. + Add command (left) and Cancel / Save (right) share one row.

🤖 Generated with Claude Code

AThraen and others added 22 commits May 12, 2026 16:40
Adds optional CREATE_SUSPENDED + Job Object (KILL_ON_JOB_CLOSE) support to
PseudoTerminal.Start so headless run-command PTYs can kill entire process
trees (cmd->dotnet->testhost) on dispose. Also captures process exit code via
GetExitCodeProcess once the Exited event fires. Existing call sites are
unchanged (useJobObject defaults to false).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements RunInstance wrapping PseudoTerminal with ANSI-stripped output
accumulation, RunState observable lifecycle, and static SSH/local command
builders tested by 7 new unit tests (82 total).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds play (▶) and chevron (▼) toolbar buttons to each terminal pane when
RunCommands are configured, a chips strip showing live/finished RunInstances,
and a collapsible output drawer with Stop/Copy/Send-to-terminal actions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements SessionRunCommandsDialog (Task 9): inline-edit rows with
label + command fields, up/down reorder buttons, default-radio column,
validation, and Cancel/Save. Replaces the Task 7 stub in
OpenRunCommandsEditor with the real ShowDialog flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reverts the run-commands integration from MainWindow / PseudoTerminal /
SessionViewModel / ShellSession / CLAUDE.md, leaving the standalone pieces
(RunCommandItem, RunCommandTemplatesService, RunInstance, SessionRunner,
SessionRunCommandsDialog) in place as untouched, orphan components.

RunInstance.cs is updated to compile against the reverted PseudoTerminal —
the useJobObject parameter and ExitCode capture are gone, so the headless
PTY relies on Dispose to terminate cmd /c and infers ExitedOk vs
ExitedFailed from whether Stop() was called rather than from a numeric
exit code.

ShellSessionRunCommandsTests.cs is removed because its target property
(ShellSession.RunCommands) was reverted out.

This is a checkpoint, not a final design — the orphan services remain
available for a future re-integration if desired.
- Darken active-session sidebar background from Catppuccin Surface2 (#585b70)
  to Surface1 (#45475a) so the muted white text reads cleanly on top.
- UpdateActiveTerminalHighlight now reads the VM's live AccentColor instead
  of the Tag stashed at build time. RepoRoot is populated asynchronously
  after GitService finishes probing, and AccentColor depends on it, so the
  cached Tag goes stale and produced a different ring color from the sidebar.
- BuildSidebarItem and BuildTerminalWrapper subscribe to vm.PropertyChanged
  for AccentColor so the per-pane accent stripes (sidebar left edge + terminal
  top border) repaint when RepoRoot lands. Without this the visible stripes
  also went stale.

Adds a CLAUDE.md note about NOT trusting 'user modified this file,
intentional' system reminders to mean user intent — that reminder fires for
any working-tree drift, including subagent or hook activity. Referencing the
2026-05-12 incident on this branch where 605 lines of just-shipped work were
silently undone by an out-of-scope subagent edit and Claude committed the
revert.
The previous commit added a new PropertyChanged subscription for AccentColor
inside BuildSidebarItem, but the existing switch-case handler at the top of
the method already handled the same property. Both fired on every change,
double-painting the stripe and double-invoking UpdateSidebarActiveState.

Consolidation:
- Remove the redundant new subscription.
- Keep BuildTerminalWrapper's dedicated AccentColor subscription, which
  remains the only handler that updates the terminal pane's inner top
  stripe (wrapper.BorderBrush). Sidebar stripe + ring stay with the
  existing switch case.
- Drop the existing case's ui.terminalWrapper.* updates since BuildTerminalWrapper's
  subscription now owns those via locally-captured references.

Minor: comment says ~6.3:1 (the actual ratio) instead of ~6.5:1.
CLAUDE.md: phrase 'Explore' as 'a read-only subagent type (Explore at time
of writing)' so the note doesn't rot if harness naming changes.
Add a header row above the editable rows showing 'Default / Label /
Command line' so users can tell which TextBox is the friendly name vs.
the actual shell invocation. Header column widths mirror the row
template. Each row's two TextBoxes also gain a ToolTip with an example
so hovering clarifies intent without taking screen real estate.
The "Default" column header was clipped by a 22px width while the word
needed ~50px. Replace it with a centered ★ glyph (Catppuccin yellow) that
doubles as visual cue, give the columns explicit widths with min-widths
so they stay readable when the dialog is resized, and bump row padding
so the input boxes don't feel cramped. The +Add and Cancel/Save buttons
share one row to reclaim some vertical real estate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-session action icons are now consolidated. The sidebar row stack
drops from 6 buttons to 3 (➕ 💤 ✕), governed by a new
SidebarActionIconsMode setting (OnHover default / Always / Hidden).
OnHover keeps the panel laid out (no text reflow on hover) but
transparent and non-interactive until the row is hovered. The terminal
toolbar gains its own ✕ close so it's a self-sufficient action surface.

The actions removed from the sidebar — Rename, Open in Explorer, Open
PowerShell here — move to the right-click context menu so they aren't
lost; rename uses the same in-place editor as double-click via a
captured Action stored in _sidebarRenameActions.

Settings UI gets a new "Sidebar action icons" dropdown in Appearance.
Live changes apply immediately to every row through _sidebarActionPanels
without a sidebar rebuild.

Deferred to a separate change: settings window reorganization and main
toolbar audit (both mentioned in #29).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AThraen AThraen merged commit d5bbeb8 into main May 12, 2026
1 check passed
@AThraen AThraen deleted the feat/run-commands branch May 12, 2026 19:27
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.

Add play button to run the session's main project

1 participant