From b20212400a3271a4bf48f1f7a779b712d7739596 Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Sat, 13 Jun 2026 11:27:13 -0700 Subject: [PATCH 1/2] fix(sidebar): increase worktree card content inset (#4885) Co-authored-by: brennanb2025 --- .../src/components/sidebar/WorktreeCard.tsx | 4 ++-- .../sidebar/worktree-list-indentation.test.ts | 9 +++++++++ .../components/sidebar/worktree-list-indentation.ts | 13 +++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/components/sidebar/WorktreeCard.tsx b/src/renderer/src/components/sidebar/WorktreeCard.tsx index 313f4157223..9ec6e8b1a95 100644 --- a/src/renderer/src/components/sidebar/WorktreeCard.tsx +++ b/src/renderer/src/components/sidebar/WorktreeCard.tsx @@ -61,6 +61,7 @@ import { } from './workspace-delete-quick-action' import { DetachedHeadBadge } from '@/components/DetachedHeadBadge' import { getWorktreeGitIdentityDisplay } from '@/lib/worktree-git-identity-display' +import { getFlushWorktreeCardPaddingLeft } from './worktree-list-indentation' import { translate } from '@/i18n/i18n' import { folderWorkspaceKey, parseWorkspaceKey } from '../../../../shared/workspace-scope' @@ -793,8 +794,7 @@ const WorktreeCard = React.memo(function WorktreeCard({ // aligned with the pre-inset layout and the repo header hierarchy. const cardStyle = flushSurface ? { - paddingLeft: - contentIndent > 0 ? `max(0.125rem, calc(${contentIndent}px - 0.625rem))` : '0.125rem' + paddingLeft: getFlushWorktreeCardPaddingLeft(contentIndent) } : contentIndent > 0 ? { paddingLeft: `calc(0.125rem + ${contentIndent}px)` } diff --git a/src/renderer/src/components/sidebar/worktree-list-indentation.test.ts b/src/renderer/src/components/sidebar/worktree-list-indentation.test.ts index 8ce4f34dc7d..a7ba3b1f80f 100644 --- a/src/renderer/src/components/sidebar/worktree-list-indentation.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list-indentation.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest' import { WORKTREE_SECTION_HEADER_PADDING_LEFT, + getFlushWorktreeCardPaddingLeft, getProjectGroupHeaderPaddingLeft, getWorktreeCardContentIndent, getWorktreeCardSurfaceInset @@ -50,4 +51,12 @@ describe('worktree list indentation', () => { it('does not inset card surfaces outside grouped views', () => { expect(getWorktreeCardSurfaceInset({ isGrouped: false, groupDepth: 4 })).toBe(0) }) + + it('pulls flush card content back by the tuned inset gap', () => { + expect(getFlushWorktreeCardPaddingLeft(20)).toBe('max(2px, calc(20px - 4px))') + }) + + it('keeps flush card content off the sidebar edge without indentation', () => { + expect(getFlushWorktreeCardPaddingLeft(0)).toBe('2px') + }) }) diff --git a/src/renderer/src/components/sidebar/worktree-list-indentation.ts b/src/renderer/src/components/sidebar/worktree-list-indentation.ts index e327e197f83..04af39ef64b 100644 --- a/src/renderer/src/components/sidebar/worktree-list-indentation.ts +++ b/src/renderer/src/components/sidebar/worktree-list-indentation.ts @@ -2,6 +2,13 @@ export const SIDEBAR_TREE_INDENT = 18 // Why: project-grouped cards need to read as children even after the card // surface inset is subtracted, while lineage rows keep the base tree step. const PROJECT_WORKTREE_CARD_EXTRA_INDENT = 2 +// Why: flush cards span the full row, so their content is pulled back from the +// raw tree indent to sit under the group header. A smaller pullback nudges +// content rightward for clearer nesting; this is the knob to tune that gap. +export const FLUSH_CARD_CONTENT_PULLBACK = 4 +// Why: even at zero indent a flush card keeps this minimal left inset so its +// surface never sits hard against the sidebar edge. +export const FLUSH_CARD_MIN_CONTENT_INSET = 2 // Why: grouped workspace cards should move their surface inward without using // the full tree step, preserving the existing compact child-card rhythm. const GROUPED_WORKTREE_CARD_SURFACE_INDENT = 14 @@ -39,3 +46,9 @@ export function getWorktreeCardSurfaceInset(args: { }): number { return args.isGrouped ? clampDepth(args.groupDepth) * GROUPED_WORKTREE_CARD_SURFACE_INDENT : 0 } + +export function getFlushWorktreeCardPaddingLeft(contentIndent: number): string { + return contentIndent > 0 + ? `max(${FLUSH_CARD_MIN_CONTENT_INSET}px, calc(${contentIndent}px - ${FLUSH_CARD_CONTENT_PULLBACK}px))` + : `${FLUSH_CARD_MIN_CONTENT_INSET}px` +} From bee8b9820a2971034445da70b270663b2ed73642 Mon Sep 17 00:00:00 2001 From: Trevin Chow Date: Sat, 13 Jun 2026 11:32:01 -0700 Subject: [PATCH 2/2] refactor: simplify runtime service helpers (#4005) * refactor: simplify runtime service helpers * fix: keep tui idle cheap checks polling --------- Co-authored-by: brennanb2025 --- src/main/runtime/orca-runtime.ts | 125 ++++++++++++++++++------------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/src/main/runtime/orca-runtime.ts b/src/main/runtime/orca-runtime.ts index d8a54d28f45..4be959fca4e 100644 --- a/src/main/runtime/orca-runtime.ts +++ b/src/main/runtime/orca-runtime.ts @@ -97,6 +97,7 @@ import { LINEAR_WRITE_BODY_CAP } from '../../shared/linear-agent-access' import type { FeatureInteractionId } from '../../shared/feature-interactions' import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry' import { FOLDER_WORKSPACE_INSTANCE_SEPARATOR, splitWorktreeId } from '../../shared/worktree-id' +import { parsePtySessionId } from '../../shared/pty-session-id-format' import { clampLinearIssueListLimit } from '../../shared/linear-issue-read-limits' import { isFolderRepo } from '../../shared/repo-kind' import { DEFAULT_WORKSPACE_STATUS_ID } from '../../shared/workspace-statuses' @@ -14504,7 +14505,12 @@ export class OrcaRuntimeService { // handler, works even for daemon terminals), and (2) the PTY foreground process // + output quiescence. The poll self-cancels when the primary OSC path fires. private startTuiIdleFallbackPoll(waiter: TerminalWaiter, leaf: RuntimeLeafRecord): void { + let foregroundPollInFlight = false waiter.pollInterval = setInterval(async () => { + if (!waiter.pollInterval) { + return + } + let startedForegroundPoll = false try { if (leaf.lastAgentStatus === 'idle') { if (waiter.pollInterval) { @@ -14555,7 +14561,14 @@ export class OrcaRuntimeService { } // Foreground process fallback: if the daemon/local provider can report // the process and it's a non-shell with quiet output, treat as idle. - if (leaf.lastAgentStatus === null && leaf.ptyId && this.ptyController) { + if ( + leaf.lastAgentStatus === null && + leaf.ptyId && + this.ptyController && + !foregroundPollInFlight + ) { + foregroundPollInFlight = true + startedForegroundPoll = true const fg = await this.ptyController.getForegroundProcess(leaf.ptyId) if (fg && !isShellProcess(fg)) { const quietMs = leaf.lastOutputAt ? Date.now() - leaf.lastOutputAt : 0 @@ -14570,12 +14583,21 @@ export class OrcaRuntimeService { } } catch { // Swallow transient PTY inspection errors and keep polling. + } finally { + if (startedForegroundPoll) { + foregroundPollInFlight = false + } } }, TUI_IDLE_POLL_INTERVAL_MS) } private startPtyTuiIdleFallbackPoll(waiter: TerminalWaiter, pty: RuntimePtyWorktreeRecord): void { + let foregroundPollInFlight = false waiter.pollInterval = setInterval(async () => { + if (!waiter.pollInterval) { + return + } + let startedForegroundPoll = false try { if (pty.lastAgentStatus === 'idle') { if (waiter.pollInterval) { @@ -14611,7 +14633,9 @@ export class OrcaRuntimeService { this.resolveWaiter(waiter, buildPtyTerminalWaitResult(waiter.handle, 'tui-idle', pty)) return } - if (pty.lastAgentStatus === null && this.ptyController) { + if (pty.lastAgentStatus === null && this.ptyController && !foregroundPollInFlight) { + foregroundPollInFlight = true + startedForegroundPoll = true const fg = await this.ptyController.getForegroundProcess(pty.ptyId) if (fg && !isShellProcess(fg)) { const quietMs = pty.lastOutputAt ? Date.now() - pty.lastOutputAt : 0 @@ -14626,6 +14650,10 @@ export class OrcaRuntimeService { } } catch { // Swallow transient PTY inspection errors and keep polling. + } finally { + if (startedForegroundPoll) { + foregroundPollInFlight = false + } } }, TUI_IDLE_POLL_INTERVAL_MS) } @@ -16566,18 +16594,13 @@ function withTimeoutResult( promise: Promise, timeoutMs: number ): Promise<{ ok: true; value: T } | { ok: false }> { - let timeout: ReturnType | null = null - return new Promise<{ ok: true; value: T } | { ok: false }>((resolve) => { - timeout = setTimeout(() => resolve({ ok: false }), timeoutMs) - promise.then( - (value) => resolve({ ok: true, value }), - () => resolve({ ok: false }) - ) - }).finally(() => { - if (timeout) { - clearTimeout(timeout) + return withTimeout( + promise.then((value) => ({ ok: true, value }) as const), + timeoutMs, + { + ok: false } - }) + ) } export function appendRecentPtyOutput(previous: string | undefined, data: string): string { @@ -17185,13 +17208,7 @@ function buildTerminalWaitResult( condition: RuntimeTerminalWaitCondition, leaf: RuntimeLeafRecord ): RuntimeTerminalWait { - return { - handle, - condition, - satisfied: true, - status: getTerminalState(leaf), - exitCode: leaf.lastExitCode - } + return buildTerminalWait(handle, condition, getTerminalState(leaf), leaf.lastExitCode) } function buildTerminalWaitBlockedResult( @@ -17200,14 +17217,13 @@ function buildTerminalWaitBlockedResult( leaf: RuntimeLeafRecord, blockedReason: RuntimeTerminalWaitBlockedReason ): RuntimeTerminalWait { - return { + return buildTerminalWait( handle, condition, - satisfied: false, - status: getTerminalState(leaf), - exitCode: leaf.lastExitCode, + getTerminalState(leaf), + leaf.lastExitCode, blockedReason - } + ) } function buildPtyTerminalWaitResult( @@ -17215,13 +17231,7 @@ function buildPtyTerminalWaitResult( condition: RuntimeTerminalWaitCondition, pty: RuntimePtyWorktreeRecord ): RuntimeTerminalWait { - return { - handle, - condition, - satisfied: true, - status: pty.connected ? 'running' : pty.lastExitCode !== null ? 'exited' : 'unknown', - exitCode: pty.lastExitCode - } + return buildTerminalWait(handle, condition, getPtyTerminalState(pty), pty.lastExitCode) } function buildPtyTerminalWaitBlockedResult( @@ -17229,17 +17239,37 @@ function buildPtyTerminalWaitBlockedResult( condition: RuntimeTerminalWaitCondition, pty: RuntimePtyWorktreeRecord, blockedReason: RuntimeTerminalWaitBlockedReason +): RuntimeTerminalWait { + return buildTerminalWait( + handle, + condition, + getPtyTerminalState(pty), + pty.lastExitCode, + blockedReason + ) +} + +function buildTerminalWait( + handle: string, + condition: RuntimeTerminalWaitCondition, + status: RuntimeTerminalState, + exitCode: number | null, + blockedReason?: RuntimeTerminalWaitBlockedReason ): RuntimeTerminalWait { return { handle, condition, - satisfied: false, - status: pty.connected ? 'running' : pty.lastExitCode !== null ? 'exited' : 'unknown', - exitCode: pty.lastExitCode, - blockedReason + satisfied: blockedReason === undefined, + status, + exitCode, + ...(blockedReason ? { blockedReason } : {}) } } +function getPtyTerminalState(pty: RuntimePtyWorktreeRecord): RuntimeTerminalState { + return pty.connected ? 'running' : pty.lastExitCode !== null ? 'exited' : 'unknown' +} + function branchSelectorMatches(branch: string, selector: string): boolean { // Why: Git worktree data can report local branches as either `refs/heads/foo` // or `foo` depending on which plumbing path produced the record. Orca's @@ -17257,12 +17287,7 @@ function normalizeBranchRef(branch: string): string { } function inferWorktreeIdFromPtyId(ptyId: string): string | null { - const separatorIndex = ptyId.lastIndexOf('@@') - if (separatorIndex <= 0) { - return null - } - const worktreeId = ptyId.slice(0, separatorIndex) - return parseRuntimeWorktreeId(worktreeId) ? worktreeId : null + return parsePtySessionId(ptyId).worktreeId } function parseRuntimeWorktreeId( @@ -17307,13 +17332,7 @@ function getLeafWorktreeStatus( ] const latestTitle = getLatestAgentCandidateTitle(...titleCandidates) const detected = latestTitle ? detectAgentStatusFromTitle(latestTitle) : leaf.lastAgentStatus - if (detected === 'permission') { - return 'permission' - } - if (detected === 'working') { - return 'working' - } - return leaf.ptyId ? 'active' : 'inactive' + return getDetectedWorktreeStatus(detected, leaf.ptyId !== null) } function classifyLatestAgentTitle( @@ -17365,7 +17384,13 @@ function getLatestAgentCandidateTitle( } function getSavedTabWorktreeStatus(title: string, hasPty: boolean): RuntimeWorktreeStatus { - const detected = detectAgentStatusFromTitle(title) + return getDetectedWorktreeStatus(detectAgentStatusFromTitle(title), hasPty) +} + +function getDetectedWorktreeStatus( + detected: AgentStatus | null, + hasPty: boolean +): RuntimeWorktreeStatus { if (detected === 'permission') { return 'permission' }