Fix delete workspace dialog overflow (#2634)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Brennan Benson 2026-05-22 13:08:11 -07:00 committed by GitHub
parent 368abf2e25
commit 8096f236aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 3 deletions

View File

@ -217,6 +217,15 @@ The pattern in `src/renderer/src/components/settings/SettingsFormControls.tsx` i
- **Control:** the shadcn primitive (`<Input>`, `<Select>`, etc.). Errors surface via `aria-invalid`; the input primitive already maps that to a destructive ring — don't paint your own.
- **Trailing metadata:** `text-[11px] text-muted-foreground` below the control (e.g., "Current: 14px · Default: 13px"), not next to the label.
### Overflow containment
Every modal, card, list row, and nested preview must stay inside its parent when content contains long paths, branch names, command names, URLs, hashes, or unbroken error text.
- **Flex/grid children that contain text need `min-w-0`.** Put it on the text column and any nested card/list wrapper that must shrink inside the parent.
- **Bound framed previews with `max-w-full overflow-hidden`.** If a nested card shows rows of paths or labels, the framed preview itself should clip instead of contributing an intrinsic width wider than the dialog/card.
- **Choose the overflow behavior deliberately.** Use `truncate` for one-line labels and paths, `[overflow-wrap:anywhere]` or `break-all` for multiline error/body text, and `overflow-x-auto` only when horizontal scrolling is the intended interaction.
- **Test with hostile strings.** Before shipping dialog/card/list changes, check at least one long local path and one long SSH-style path so local-only happy paths don't hide layout bugs.
### Scrollbars
Three scrollbar classes are defined globally in `main.css`:

View File

@ -127,4 +127,19 @@ describe('DeleteWorktreeDialog lineage copy', () => {
expect(markup).toContain('Delete Parent Only')
expect(markup).not.toContain('Don&apos;t ask again')
})
it('keeps long child workspace paths constrained inside the lineage notice', async () => {
const child = makeWorktree(
'docs-file-upload-discovery-with-a-very-long-name',
'/Users/jinjingliang/Documents/projects/agent-slack/docs-file-upload-discovery-with-a-very-long-path-segment'
)
const { DeleteWorktreeLineageNotice } = await import('./DeleteWorktreeLineageNotice')
const markup = renderToStaticMarkup(<DeleteWorktreeLineageNotice descendants={[child]} />)
expect(markup).toContain('min-w-0 max-w-full overflow-hidden rounded-md')
expect(markup).toContain('mt-2 min-w-0 max-w-full space-y-1 overflow-hidden')
expect(markup).toContain('min-w-0 overflow-hidden')
expect(markup).toContain('truncate text-muted-foreground')
})
})

View File

@ -15,7 +15,7 @@ export function DeleteWorktreeLineageNotice({
}
return (
<div className="rounded-md border border-border/70 bg-muted/35 px-3 py-2 text-xs">
<div className="min-w-0 max-w-full overflow-hidden rounded-md border border-border/70 bg-muted/35 px-3 py-2 text-xs">
<div className="flex items-start gap-2">
<Workflow className="mt-0.5 size-3.5 shrink-0 text-muted-foreground" />
<div className="min-w-0 flex-1">
@ -26,9 +26,11 @@ export function DeleteWorktreeLineageNotice({
? '1 child workspace will stay in Orca and on disk.'
: `${childWorkspaceCount} child workspaces will stay in Orca and on disk.`}
</div>
<div className="mt-2 space-y-1 rounded-sm border border-border/60 bg-background/60 px-2 py-1.5">
{/* Why: long nowrap paths can otherwise give this grid child an
intrinsic width wider than the modal. */}
<div className="mt-2 min-w-0 max-w-full space-y-1 overflow-hidden rounded-sm border border-border/60 bg-background/60 px-2 py-1.5">
{descendants.slice(0, 4).map((child) => (
<div key={child.id} className="min-w-0">
<div key={child.id} className="min-w-0 overflow-hidden">
<div className="truncate font-medium text-foreground">{child.displayName}</div>
<div className="truncate text-muted-foreground">{child.path}</div>
</div>