Align folder rows with new card style

This commit is contained in:
brennanb2025
2026-06-22 22:01:19 -07:00
parent 1f164daf71
commit 77e76144a2
9 changed files with 302 additions and 36 deletions
@@ -340,6 +340,7 @@ function makeFolderWorkspacePathStatusState(): Record<string, unknown> {
function setFolderWorkspaceFixtureState(
options: {
createdFrom?: ProjectGroup['createdFrom']
experimentalNewWorktreeCardStyle?: boolean
nestedGroup?: boolean
} = {}
): void {
@@ -405,7 +406,9 @@ function setFolderWorkspaceFixtureState(
setRenamingWorktreeId: vi.fn(),
setShowSleepingWorkspaces: vi.fn(),
setSortBy: vi.fn(),
settings: null,
settings: options.experimentalNewWorktreeCardStyle
? { experimentalNewWorktreeCardStyle: true }
: null,
renamingWorktreeId: null,
showSleepingWorkspaces: true,
sortBy: 'manual',
@@ -1040,6 +1043,25 @@ describe('WorktreeList lineage child card renderer', () => {
).toBe(20)
})
it('uses comparable new-card worktree geometry for experimental folder workspace rows', async () => {
setFolderWorkspaceFixtureState({ experimentalNewWorktreeCardStyle: true })
const markup = await renderWorktreeListMarkup()
const folderWorktreeId = folderWorkspaceKey('folder-workspace-1')
const cardOpeningTag = getCardOpeningTag(markup, folderWorktreeId)
const surfaceOpeningTag = getFolderWorkspaceSurfaceOpeningTag(markup, 'folder-workspace-1')
const cardContentIndent = getDataNumber(cardOpeningTag, 'data-content-indent')
expect(surfaceOpeningTag).toContain('padding-left:14px')
expect(cardOpeningTag).toContain('data-content-indent="16"')
expect(cardOpeningTag).toContain('data-flush-surface="true"')
expect(
getFlushCardContentStart({
cardContentIndent,
surfaceInset: getPaddingLeft(surfaceOpeningTag)
})
).toBe(30)
})
it('preserves manual folder workspace indentation outside folder-scanned groups', async () => {
setFolderWorkspaceFixtureState({ createdFrom: 'manual' })
const markup = await renderWorktreeListMarkup()
@@ -223,8 +223,7 @@ import {
LINEAGE_CHILDREN_INLINE_OFFSET,
getFolderBackedRepoWorktreeCardContentIndent,
getFolderBackedRepoWorktreeCardSurfaceInset,
getFolderWorkspaceCardContentIndent,
getFolderWorkspaceCardSurfaceInset,
getFolderWorkspaceRowGeometry,
getLineageChildrenInlineStyle,
getLineageNestedRowGeometry,
getProjectGroupHeaderPaddingLeft,
@@ -4492,28 +4491,13 @@ const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktreeViewp
folderWorkspacePathStatus.reason === 'ambiguous-connection')
const isFolderBackedWorkspaceChild =
groupBy === 'repo' && folderWorkspaceRow.projectGroup.createdFrom === 'folder-scan'
const contentIndent = isFolderBackedWorkspaceChild
? getFolderWorkspaceCardContentIndent({
groupDepth: folderWorkspaceRow.groupDepth
})
: getWorktreeCardContentIndent({
isGrouped: groupBy !== 'none',
groupDepth: folderWorkspaceRow.groupDepth,
lineageDepth: folderWorkspaceRow.depth
})
// Why: folder workspace surfaces should step inward with their
// project-group nesting, matching lineage child card surfaces
// instead of spanning from the sidebar edge at every depth.
const surfaceInset = isFolderBackedWorkspaceChild
? getFolderWorkspaceCardSurfaceInset({
isGrouped: true,
groupDepth: folderWorkspaceRow.groupDepth
})
: getWorktreeCardSurfaceInset({
isGrouped: groupBy !== 'none',
groupDepth: folderWorkspaceRow.groupDepth
})
const insetContentIndent = Math.max(0, contentIndent - surfaceInset)
const { surfaceInset, cardContentIndent } = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: newCardStyle,
isFolderBackedWorkspaceChild,
isGrouped: groupBy !== 'none',
groupDepth: folderWorkspaceRow.groupDepth,
lineageDepth: folderWorkspaceRow.depth
})
return (
<div
key={vItem.key}
@@ -4544,7 +4528,7 @@ const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktreeViewp
repo={undefined}
isActive={activeWorktreeId === folderWorktree.id}
isCurrentWorktree={currentWorktreeId === folderWorktree.id}
contentIndent={insetContentIndent}
contentIndent={cardContentIndent}
flushSurface
nativeDragEnabled={false}
onImmediateActivate={
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import {
FLUSH_CARD_MIN_CONTENT_INSET,
FLUSH_CARD_CONTENT_PULLBACK,
LINEAGE_CHILDREN_INLINE_OFFSET,
LINEAGE_IMMEDIATE_PARENT_STEP,
LINEAGE_NESTED_ROW_SURFACE_INSET,
@@ -10,6 +11,7 @@ import {
getFolderBackedRepoWorktreeCardSurfaceInset,
getFolderWorkspaceCardContentIndent,
getFolderWorkspaceCardSurfaceInset,
getFolderWorkspaceRowGeometry,
getFlushWorktreeCardPaddingLeft,
getLineageChildrenInlineStyle,
getLineageEffectiveChildStart,
@@ -19,6 +21,17 @@ import {
getWorktreeCardSurfaceInset
} from './worktree-list-indentation'
function getFlushCardContentStart(args: {
surfaceInset: number
cardContentIndent: number
}): number {
return (
args.surfaceInset +
WORKTREE_CARD_SURFACE_MARGIN +
Math.max(FLUSH_CARD_MIN_CONTENT_INSET, args.cardContentIndent - FLUSH_CARD_CONTENT_PULLBACK)
)
}
describe('worktree list indentation', () => {
it('keeps ungrouped workspaces flush with the list', () => {
expect(getWorktreeCardContentIndent({ isGrouped: false, groupDepth: 4, lineageDepth: 0 })).toBe(
@@ -76,6 +89,118 @@ describe('worktree list indentation', () => {
expect(getFolderWorkspaceCardSurfaceInset({ isGrouped: false, groupDepth: 2 })).toBe(0)
})
it('preserves legacy folder-scanned folder workspace row geometry', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: false,
isFolderBackedWorkspaceChild: true,
isGrouped: true,
groupDepth: 1,
lineageDepth: 0
})
expect(geometry).toEqual({
surfaceInset: 14,
cardContentIndent: 6
})
expect(getFlushCardContentStart(geometry)).toBe(20)
})
it('preserves legacy nested folder-scanned folder workspace row geometry', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: false,
isFolderBackedWorkspaceChild: true,
isGrouped: true,
groupDepth: 2,
lineageDepth: 0
})
expect(geometry).toEqual({
surfaceInset: 24,
cardContentIndent: 6
})
expect(getFlushCardContentStart(geometry)).toBe(30)
})
it('preserves legacy manual grouped folder workspace row geometry', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: false,
isFolderBackedWorkspaceChild: false,
isGrouped: true,
groupDepth: 1,
lineageDepth: 0
})
expect(geometry).toEqual({
surfaceInset: 14,
cardContentIndent: 24
})
expect(getFlushCardContentStart(geometry)).toBe(38)
})
it('uses comparable repo worktree geometry for experimental folder-scanned folder workspaces', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: true,
isFolderBackedWorkspaceChild: true,
isGrouped: true,
groupDepth: 1,
lineageDepth: 0
})
expect(geometry).toEqual({
surfaceInset: 14,
cardContentIndent: 16
})
expect(getFlushCardContentStart(geometry)).toBe(30)
})
it('uses comparable repo worktree geometry for experimental nested folder workspaces', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: true,
isFolderBackedWorkspaceChild: true,
isGrouped: true,
groupDepth: 4,
lineageDepth: 3
})
expect(geometry).toEqual({
surfaceInset: 54,
cardContentIndent: 6
})
expect(getFlushCardContentStart(geometry)).toBe(60)
})
it('keeps experimental manual grouped folder workspaces on normal worktree geometry', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: true,
isFolderBackedWorkspaceChild: false,
isGrouped: true,
groupDepth: 1,
lineageDepth: 0
})
expect(geometry).toEqual({
surfaceInset: 14,
cardContentIndent: 24
})
expect(getFlushCardContentStart(geometry)).toBe(38)
})
it('keeps experimental flat folder workspaces on normal worktree geometry', () => {
const geometry = getFolderWorkspaceRowGeometry({
experimentalNewWorktreeCardStyle: true,
isFolderBackedWorkspaceChild: false,
isGrouped: false,
groupDepth: 3,
lineageDepth: 0
})
expect(geometry).toEqual({
surfaceInset: 0,
cardContentIndent: 0
})
expect(getFlushCardContentStart(geometry)).toBe(6)
})
it('caps header indentation separately from workspace content indentation', () => {
expect(getProjectGroupHeaderPaddingLeft(100)).toBe(70)
})
@@ -99,6 +99,61 @@ export function getFolderWorkspaceCardSurfaceInset(args: {
return Math.min(genericSurfaceInset, Math.max(0, maxSurfaceInset))
}
export function getFolderWorkspaceRowGeometry(args: {
experimentalNewWorktreeCardStyle: boolean
isFolderBackedWorkspaceChild: boolean
isGrouped: boolean
groupDepth: number
lineageDepth: number
}): {
surfaceInset: number
cardContentIndent: number
} {
if (args.experimentalNewWorktreeCardStyle && args.isFolderBackedWorkspaceChild) {
// Why: standalone folder workspace rows do not get a lineage wrapper
// offset, so align them to the comparable folder-backed repo row anchor.
const contentAnchor = getFolderBackedRepoWorktreeCardContentIndent({
groupDepth: args.groupDepth,
lineageDepth: 0
})
const surfaceInset = getFolderBackedRepoWorktreeCardSurfaceInset({
groupDepth: args.groupDepth,
lineageDepth: 0
})
return {
surfaceInset,
cardContentIndent: Math.max(0, contentAnchor - surfaceInset)
}
}
const contentIndent = args.isFolderBackedWorkspaceChild
? getFolderWorkspaceCardContentIndent({
groupDepth: args.groupDepth
})
: getWorktreeCardContentIndent({
isGrouped: args.isGrouped,
groupDepth: args.groupDepth,
lineageDepth: args.lineageDepth
})
// Why: legacy folder-scanned folder workspaces keep their compact anchor,
// while all other folder rows share the normal worktree row surface path.
const surfaceInset = args.isFolderBackedWorkspaceChild
? getFolderWorkspaceCardSurfaceInset({
isGrouped: true,
groupDepth: args.groupDepth
})
: getWorktreeCardSurfaceInset({
isGrouped: args.isGrouped,
groupDepth: args.groupDepth
})
return {
surfaceInset,
cardContentIndent: Math.max(0, contentIndent - surfaceInset)
}
}
export function getWorktreeCardSurfaceInset(args: {
isGrouped: boolean
groupDepth: number
+18 -2
View File
@@ -6255,7 +6255,19 @@
"ab20575a8a": "off",
"ab3a1f9068": "wsl.exe",
"ask_before_closing_running_terminals_title": "Ask Before Closing Running Terminals",
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent."
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed.",
"helper": "Adjust how wheel input feels in scrollback and in mouse-aware terminal apps.",
"reset": "Reset",
"normal": "Normal",
"normalDescription": "Scrollback wheel multiplier.",
"fast": "Fast",
"fastDescription": "Extra multiplier while scrolling with a modifier key.",
"tui": "TUI",
"tuiDescription": "Discrete wheel reports for full-screen terminal apps."
}
},
"TerminalSettingsPreview": {
"a63953a48a": "Preview {{value0}} theme",
@@ -7891,7 +7903,11 @@
"0fe0073f0c": "Default terminal font size for new panes and live updates.",
"5930244899": "Font Size",
"ask_before_closing_running_terminals_title": "Ask Before Closing Running Terminals",
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent."
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed."
}
},
"windows": {
"search": {
+18 -2
View File
@@ -6218,7 +6218,19 @@
"ab20575a8a": "apagado",
"ab3a1f9068": "wsl.exe",
"ask_before_closing_running_terminals_title": "Ask Before Closing Running Terminals",
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent."
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed.",
"helper": "Adjust how wheel input feels in scrollback and in mouse-aware terminal apps.",
"reset": "Reset",
"normal": "Normal",
"normalDescription": "Scrollback wheel multiplier.",
"fast": "Fast",
"fastDescription": "Extra multiplier while scrolling with a modifier key.",
"tui": "TUI",
"tuiDescription": "Discrete wheel reports for full-screen terminal apps."
}
},
"TerminalSettingsPreview": {
"a63953a48a": "Vista previa del tema {{value0}}",
@@ -7854,7 +7866,11 @@
"keyword_custom": "custom"
},
"ask_before_closing_running_terminals_title": "Ask Before Closing Running Terminals",
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent."
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed."
}
},
"windows": {
"search": {
+18 -2
View File
@@ -6240,7 +6240,19 @@
"ab20575a8a": "オフ",
"ab3a1f9068": "wsl.exe",
"ask_before_closing_running_terminals_title": "Ask Before Closing Running Terminals",
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent."
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed.",
"helper": "Adjust how wheel input feels in scrollback and in mouse-aware terminal apps.",
"reset": "Reset",
"normal": "Normal",
"normalDescription": "Scrollback wheel multiplier.",
"fast": "Fast",
"fastDescription": "Extra multiplier while scrolling with a modifier key.",
"tui": "TUI",
"tuiDescription": "Discrete wheel reports for full-screen terminal apps."
}
},
"TerminalSettingsPreview": {
"a63953a48a": "{{value0}} テーマのプレビュー",
@@ -7876,7 +7888,11 @@
"0fe0073f0c": "新規ペインとライブ アップデートのデフォルトの terminal フォント サイズ。",
"5930244899": "フォントサイズ",
"ask_before_closing_running_terminals_title": "Ask Before Closing Running Terminals",
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent."
"ask_before_closing_running_terminals_description": "Show a confirmation before closing a terminal that has a running command or agent.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed."
}
},
"windows": {
"search": {
+18 -2
View File
@@ -6203,7 +6203,19 @@
"ab20575a8a": "끄기",
"ab3a1f9068": "wsl.exe",
"ask_before_closing_running_terminals_title": "실행 중인 Terminals을 닫기 전에 확인",
"ask_before_closing_running_terminals_description": "실행 중인 명령이나 agent가 있는 terminal을 닫기 전에 확인 창을 표시합니다."
"ask_before_closing_running_terminals_description": "실행 중인 명령이나 agent가 있는 terminal을 닫기 전에 확인 창을 표시합니다.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed.",
"helper": "Adjust how wheel input feels in scrollback and in mouse-aware terminal apps.",
"reset": "Reset",
"normal": "Normal",
"normalDescription": "Scrollback wheel multiplier.",
"fast": "Fast",
"fastDescription": "Extra multiplier while scrolling with a modifier key.",
"tui": "TUI",
"tuiDescription": "Discrete wheel reports for full-screen terminal apps."
}
},
"TerminalSettingsPreview": {
"a63953a48a": "{{value0}} 테마 미리보기",
@@ -7839,7 +7851,11 @@
"keyword_custom": "custom"
},
"ask_before_closing_running_terminals_title": "실행 중인 Terminals을 닫기 전에 확인",
"ask_before_closing_running_terminals_description": "실행 중인 명령이나 agent가 있는 terminal을 닫기 전에 확인을 표시합니다."
"ask_before_closing_running_terminals_description": "실행 중인 명령이나 agent가 있는 terminal을 닫기 전에 확인을 표시합니다.",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed."
}
},
"windows": {
"search": {
+18 -2
View File
@@ -6203,7 +6203,19 @@
"ab20575a8a": "关",
"ab3a1f9068": "执行程序",
"ask_before_closing_running_terminals_title": "关闭运行中的 Terminal 前询问",
"ask_before_closing_running_terminals_description": "在关闭有运行中命令或 agent 的 terminal 前显示确认。"
"ask_before_closing_running_terminals_description": "在关闭有运行中命令或 agent 的 terminal 前显示确认。",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed.",
"helper": "Adjust how wheel input feels in scrollback and in mouse-aware terminal apps.",
"reset": "Reset",
"normal": "Normal",
"normalDescription": "Scrollback wheel multiplier.",
"fast": "Fast",
"fastDescription": "Extra multiplier while scrolling with a modifier key.",
"tui": "TUI",
"tuiDescription": "Discrete wheel reports for full-screen terminal apps."
}
},
"TerminalSettingsPreview": {
"a63953a48a": "预览 {{value0}} 主题",
@@ -7839,7 +7851,11 @@
"keyword_custom": "自定义"
},
"ask_before_closing_running_terminals_title": "关闭运行中的 Terminal 前询问",
"ask_before_closing_running_terminals_description": "在关闭有运行中命令或 agent 的 terminal 前显示确认。"
"ask_before_closing_running_terminals_description": "在关闭有运行中命令或 agent 的 terminal 前显示确认。",
"scrollSpeed": {
"title": "Scroll Speed",
"description": "Tune normal terminal scrollback, fast modifier scrolling, and full-screen TUI wheel speed."
}
},
"windows": {
"search": {