Align experimental worktree cards with status lane pullback (#6158)
- Apply extra left-padding pullback to experimental cards when the status lane is shown, keeping title text aligned to tree steps. - Use negative left margin on shallow rows when the minimum content inset clamps the padding adjustment. - Resolve experimental card style setting dynamically in the folder workspace panel instead of hardcoding it.
This commit is contained in:
parent
b43b4356db
commit
9607b30fde
|
|
@ -13,6 +13,9 @@ import {
|
|||
type MockStoreState = {
|
||||
activeWorktreeId: string | null
|
||||
activeWorkspaceKey: string | null
|
||||
settings?: {
|
||||
experimentalNewWorktreeCardStyle?: boolean
|
||||
}
|
||||
folderWorkspaces: {
|
||||
id: string
|
||||
name: string
|
||||
|
|
@ -28,6 +31,9 @@ const testState = vi.hoisted(() => ({
|
|||
store: {
|
||||
activeWorktreeId: null,
|
||||
activeWorkspaceKey: null,
|
||||
settings: {
|
||||
experimentalNewWorktreeCardStyle: true
|
||||
},
|
||||
folderWorkspaces: [],
|
||||
workspaceLineageByChildKey: {},
|
||||
worktreeLineageById: {},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ function stopNestedWorktreeCardBubble(event: React.SyntheticEvent<HTMLElement>):
|
|||
export default function FolderWorkspaceWorktreesPanel(): React.JSX.Element {
|
||||
const activeWorktreeId = useAppStore((s) => s.activeWorktreeId)
|
||||
const activeWorkspaceKey = useAppStore((s) => s.activeWorkspaceKey)
|
||||
const experimentalNewWorktreeCardStyle =
|
||||
useAppStore((s) => s.settings?.experimentalNewWorktreeCardStyle) === true
|
||||
const folderWorkspaces = useAppStore((s) => s.folderWorkspaces)
|
||||
const workspaceLineageByChildKey = useAppStore((s) => s.workspaceLineageByChildKey)
|
||||
const worktreeLineageById = useAppStore((s) => s.worktreeLineageById)
|
||||
|
|
@ -58,9 +60,7 @@ export default function FolderWorkspaceWorktreesPanel(): React.JSX.Element {
|
|||
const safeLineageChildren = lineageChildren.filter((child) => !nextAncestorIds.has(child.id))
|
||||
const hasSafeLineageChildren = safeLineageChildren.length > 0
|
||||
const lineageGeometry = getLineageNestedRowGeometry({
|
||||
// Why: folder-workspace lineage always uses the in-card child geometry,
|
||||
// regardless of the main sidebar experimental-card toggle.
|
||||
experimentalNewWorktreeCardStyle: true,
|
||||
experimentalNewWorktreeCardStyle,
|
||||
inheritedCardContentIndent: 0,
|
||||
lineageDepth: ancestorIds.size
|
||||
})
|
||||
|
|
|
|||
|
|
@ -63,7 +63,10 @@ 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 {
|
||||
getFlushWorktreeCardPaddingLeft,
|
||||
getNewCardStyleParentContentMarginLeft
|
||||
} from './worktree-list-indentation'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { recordRendererCrashBreadcrumb } from '@/lib/crash-diagnostics'
|
||||
import { folderWorkspaceKey, parseWorkspaceKey } from '../../../../shared/workspace-scope'
|
||||
|
|
@ -1155,11 +1158,16 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
: undefined
|
||||
// Why: sidebar rows need a small surface inset, while their content remains
|
||||
// aligned with the pre-inset layout and the repo header hierarchy.
|
||||
const applyNewCardStyleStatusLaneOffset = newCardStyle && showCombinedStatusSlot
|
||||
const cardPaddingLeft = flushSurface
|
||||
? getFlushWorktreeCardPaddingLeft(contentIndent)
|
||||
? getFlushWorktreeCardPaddingLeft(contentIndent, applyNewCardStyleStatusLaneOffset)
|
||||
: contentIndent > 0
|
||||
? `calc(0.125rem + ${contentIndent}px)`
|
||||
: null
|
||||
const parentContentMarginLeft =
|
||||
flushSurface && applyNewCardStyleStatusLaneOffset
|
||||
? getNewCardStyleParentContentMarginLeft(contentIndent)
|
||||
: 0
|
||||
const cardStyle = cardPaddingLeft ? { paddingLeft: cardPaddingLeft } : undefined
|
||||
const detailsAndPortsContent =
|
||||
hasDetails || hasPorts ? (
|
||||
|
|
@ -1223,6 +1231,9 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
'flex w-full min-w-0 gap-0.5 pl-0',
|
||||
titleOnlyCard ? 'items-center' : 'items-start'
|
||||
)}
|
||||
style={
|
||||
parentContentMarginLeft < 0 ? { marginLeft: `${parentContentMarginLeft}px` } : undefined
|
||||
}
|
||||
data-worktree-card-parent-content=""
|
||||
>
|
||||
{showCombinedStatusSlot ? (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
FLUSH_CARD_CONTENT_PULLBACK,
|
||||
FLUSH_CARD_MIN_CONTENT_INSET,
|
||||
NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK,
|
||||
LINEAGE_CHILDREN_INLINE_OFFSET,
|
||||
LINEAGE_IMMEDIATE_PARENT_STEP,
|
||||
LINEAGE_NESTED_ROW_SURFACE_INSET,
|
||||
|
|
@ -97,8 +99,15 @@ describe('worktree list indentation', () => {
|
|||
expect(getFlushWorktreeCardPaddingLeft(20)).toBe('max(2px, calc(20px - 4px))')
|
||||
})
|
||||
|
||||
it('pulls experimental flush cards back further for the fixed status lane', () => {
|
||||
expect(getFlushWorktreeCardPaddingLeft(20, true)).toBe(
|
||||
`max(2px, calc(20px - ${FLUSH_CARD_CONTENT_PULLBACK + NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK}px))`
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps flush card content off the sidebar edge without indentation', () => {
|
||||
expect(getFlushWorktreeCardPaddingLeft(0)).toBe('2px')
|
||||
expect(getFlushWorktreeCardPaddingLeft(0, true)).toBe('2px')
|
||||
})
|
||||
|
||||
it('derives the lineage parent-child step from the pre-refactor grouped-card anchor', () => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ const PROJECT_WORKTREE_CARD_EXTRA_INDENT = 2
|
|||
// 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: experimental cards reserve a fixed status lane inside the padded
|
||||
// content box; pull the box back so title/meta text stay on the tree step.
|
||||
export const NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK = 6
|
||||
// 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
|
||||
|
|
@ -106,12 +109,43 @@ export function getWorktreeCardSurfaceInset(args: {
|
|||
return args.isGrouped ? clampDepth(args.groupDepth) * GROUPED_WORKTREE_CARD_SURFACE_INDENT : 0
|
||||
}
|
||||
|
||||
export function getFlushWorktreeCardPaddingLeft(contentIndent: number): string {
|
||||
export function getFlushWorktreeCardPaddingLeft(
|
||||
contentIndent: number,
|
||||
applyNewCardStyleStatusLaneOffset = false
|
||||
): string {
|
||||
const pullback =
|
||||
FLUSH_CARD_CONTENT_PULLBACK +
|
||||
(applyNewCardStyleStatusLaneOffset ? NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK : 0)
|
||||
return contentIndent > 0
|
||||
? `max(${FLUSH_CARD_MIN_CONTENT_INSET}px, calc(${contentIndent}px - ${FLUSH_CARD_CONTENT_PULLBACK}px))`
|
||||
? `max(${FLUSH_CARD_MIN_CONTENT_INSET}px, calc(${contentIndent}px - ${pullback}px))`
|
||||
: `${FLUSH_CARD_MIN_CONTENT_INSET}px`
|
||||
}
|
||||
|
||||
export function getNewCardStyleParentContentMarginLeft(contentIndent: number): number {
|
||||
if (contentIndent <= 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
const legacyInnerPadding = Math.max(
|
||||
FLUSH_CARD_MIN_CONTENT_INSET,
|
||||
contentIndent - FLUSH_CARD_CONTENT_PULLBACK
|
||||
)
|
||||
const newInnerPadding = Math.max(
|
||||
FLUSH_CARD_MIN_CONTENT_INSET,
|
||||
contentIndent - FLUSH_CARD_CONTENT_PULLBACK - NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK
|
||||
)
|
||||
const paddingShift = legacyInnerPadding - newInnerPadding
|
||||
const remainingShift = NEW_CARD_STYLE_STATUS_LANE_EXTRA_PULLBACK - paddingShift
|
||||
if (remainingShift <= 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Why: shallow rows hit the flush-card padding floor; finish the status-lane
|
||||
// offset with margin, but never pull content past the card's inner edge.
|
||||
const rawMargin = -remainingShift
|
||||
return Math.max(-newInnerPadding, rawMargin)
|
||||
}
|
||||
|
||||
export function getLineageNestedRowGeometry(args: {
|
||||
experimentalNewWorktreeCardStyle: boolean
|
||||
inheritedCardContentIndent: number
|
||||
|
|
|
|||
Loading…
Reference in New Issue