feat: per-session run commands + sidebar action-icon cleanup#34
Merged
Conversation
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.
This reverts commit 5d5a762.
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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+F5run/stop the default. Runs execute in a separate headlessPseudoTerminalwrapped 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 offersStop,Copy, andSend to terminal(which wraps in a fenced preamble for Claude parents and falls back to clipboard for non-Claude shells). New sessions are seeded fromRunCommandTemplatesServicebased 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
SidebarActionIconsModesetting:OnHover(default) /Always/Hidden.OnHoverkeeps 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
dotnet runoutput), and a session carries a list of labelled commands rather than a singleRunCommand: string?. Both were validated by issue comments and confirmed during implementation.Design notes
PseudoTerminalgaineduseJobObject: trueplusJOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEso child trees (npm test,dotnet watch, …) die with the wrapping PTY.RunInstancealways opts in; the parent session PTY does not.RunInstancestrips ANSI and buffers up to 1 MB per run. Not persisted — runs are ephemeral and killed on session close / sleep / app exit.RunCommandItem.EnsureSingleDefault(list)is called on save and on seed; the dialog uses aRadioButtongroup so the invariant survives editing.SleepSessionexplicitly callsvm.Runner.StopAll()before the VM disposes so child processes die before the WebView2 / xterm controls start tearing down.Opacity+IsHitTestVisiblebut keepsVisibility=Visibleso the row never reflows when the cursor enters or leaves. Hidden mode collapses the panel and reclaims the horizontal space for the text._sidebarActionPanelsdictionary letsSettingsButton_Clickflip every row to the new mode immediately — no sidebar rebuild needed._sidebarRenameActionscaptures 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
--cleanfor an isolated state:Run commands (#33)
⏹ 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.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.Session commands ▸lists each command with the default labelled(default); clicking runs it.Edit commands…opens the dialog.ping -t 8.8.8.8); sleep the session — the chip clears, no orphanping.exesurvives in Task Manager. Same for close. Same for app exit while a run is in flight.Sidebar action-icon cleanup (#29)
➕ 💤 ✕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.Rename session. The in-place editor opens on the row (same path as double-click); Enter saves, Escape cancels.▶ ▼ 💤 📝 >_ 📁cluster and the status dot.Run Commands dialog redesign
MinWidth=560,MinHeight=360.+ Add command(left) andCancel/Save(right) share one row.🤖 Generated with Claude Code