mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
feat(tab-group): make the split pane landing rows outline buttons (#40469)
This commit is contained in:
@@ -23,12 +23,9 @@ import {
|
||||
ensureSimulatorTab,
|
||||
getSimulatorTabForWorktree
|
||||
} from '@/lib/simulator/tabs/ensure-simulator-tab'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { SplitPaneLandingAgentRow } from './SplitPaneLandingAgentRow'
|
||||
import {
|
||||
LANDING_ROW_CLASS,
|
||||
LandingRowShortcut,
|
||||
moveLandingRowFocus
|
||||
} from './split-pane-landing-row'
|
||||
import { LandingRowLabel, LandingRowShortcut, moveLandingRowFocus } from './split-pane-landing-row'
|
||||
|
||||
/** Fills an empty split pane so the user picks what opens there; each action creates into this group. */
|
||||
export function SplitPaneLanding({
|
||||
@@ -142,12 +139,12 @@ export function SplitPaneLanding({
|
||||
return (
|
||||
<div className="absolute inset-0 overflow-auto scrollbar-sleek bg-background">
|
||||
<div className="mx-auto flex min-h-full w-full max-w-sm flex-col justify-center px-4 py-10">
|
||||
<div className="mb-3 px-2.5 text-sm text-muted-foreground">
|
||||
<div className="mb-3 text-sm text-muted-foreground">
|
||||
{t('components.tabGroup.splitPaneLanding.heading', 'Open in this pane')}
|
||||
</div>
|
||||
<div
|
||||
ref={listRef}
|
||||
className="flex flex-col gap-1"
|
||||
className="flex flex-col gap-2"
|
||||
onKeyDown={(event) => moveLandingRowFocus(event, listRef.current)}
|
||||
>
|
||||
<SplitPaneLandingAgentRow
|
||||
@@ -160,7 +157,7 @@ export function SplitPaneLanding({
|
||||
))}
|
||||
{canReopen ? (
|
||||
<>
|
||||
<div className="mx-2.5 my-1.5 h-px bg-border" />
|
||||
<div className="my-1 h-px bg-border" />
|
||||
<LandingRowButton
|
||||
row={{
|
||||
id: 'reopen',
|
||||
@@ -177,13 +174,17 @@ export function SplitPaneLanding({
|
||||
) : null}
|
||||
</div>
|
||||
{onClosePane ? (
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
className="mt-5 self-center rounded-md px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="mt-4 self-center"
|
||||
onClick={onClosePane}
|
||||
>
|
||||
{t('components.tabGroup.splitPaneLanding.closePane', 'Close pane')}
|
||||
</button>
|
||||
<span className="text-muted-foreground">
|
||||
{t('components.tabGroup.splitPaneLanding.closePane', 'Close pane')}
|
||||
</span>
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
@@ -201,10 +202,17 @@ type LandingRow = {
|
||||
function LandingRowButton({ row }: { row: LandingRow }): React.JSX.Element {
|
||||
const Icon = row.icon
|
||||
return (
|
||||
<button type="button" data-landing-row className={LANDING_ROW_CLASS} onClick={row.onSelect}>
|
||||
<Icon className="size-4 text-muted-foreground" />
|
||||
<span className="min-w-0 flex-1 truncate text-left">{row.label}</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
data-landing-row
|
||||
className="w-full justify-start"
|
||||
onClick={row.onSelect}
|
||||
>
|
||||
<Icon className="text-muted-foreground" />
|
||||
<LandingRowLabel label={row.label} />
|
||||
<LandingRowShortcut shortcut={row.shortcut} />
|
||||
</button>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ import { Bot, ChevronDown } from 'lucide-react'
|
||||
import { useAppStore } from '../../store'
|
||||
import { useShortcutKeyDetails } from '@/hooks/useShortcutLabel'
|
||||
import { useTranslate } from '@/i18n/use-translate'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ButtonGroup } from '@/components/ui/button-group'
|
||||
import { getAgentCatalog } from '@/lib/agent-catalog/agent-catalog'
|
||||
import { AgentIcon } from '@/components/agent/icons/AgentIcon'
|
||||
import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface'
|
||||
@@ -18,9 +19,9 @@ import {
|
||||
resolveTerminalAgentTabShortcut
|
||||
} from '../terminal-agent-tab-shortcut'
|
||||
import { launchAgentTabShortcut } from '../agent-tab-shortcut-launch'
|
||||
import { LANDING_ROW_CLASS, LandingRowShortcut } from './split-pane-landing-row'
|
||||
import { LandingRowLabel, LandingRowShortcut } from './split-pane-landing-row'
|
||||
|
||||
/** The default agent as one row; the chevron opens the + menu's agent list for any other. */
|
||||
/** The default agent as a split button; the chevron opens the + menu's agent list for any other. */
|
||||
export function SplitPaneLandingAgentRow({
|
||||
worktreeId,
|
||||
groupId,
|
||||
@@ -59,36 +60,32 @@ export function SplitPaneLandingAgentRow({
|
||||
}
|
||||
|
||||
return (
|
||||
// Why: a stretched button keeps the row one click target while the chevron stays its own button.
|
||||
<div
|
||||
className={cn(
|
||||
LANDING_ROW_CLASS,
|
||||
'relative has-[[data-landing-row]:focus-visible]:bg-accent has-[[data-landing-row]:focus-visible]:text-accent-foreground'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
<ButtonGroup className="w-full">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="lg"
|
||||
data-landing-row
|
||||
aria-label={label}
|
||||
className="absolute inset-0 rounded-md focus-visible:outline-none"
|
||||
className="min-w-0 flex-1 justify-start"
|
||||
onClick={launchDefault}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
<span className="pointer-events-none flex text-muted-foreground">
|
||||
{defaultAgent ? <AgentIcon agent={defaultAgent} size={16} /> : <Bot className="size-4" />}
|
||||
</span>
|
||||
<span className="pointer-events-none min-w-0 truncate">{label}</span>
|
||||
>
|
||||
{defaultAgent ? <AgentIcon agent={defaultAgent} size={16} /> : <Bot />}
|
||||
<LandingRowLabel label={label} />
|
||||
<LandingRowShortcut shortcut={shortcut} />
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon-lg"
|
||||
aria-label={t('components.tabGroup.splitPaneLanding.chooseAgent', 'Choose agent')}
|
||||
className="relative -ml-1.5 flex size-6 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-foreground/10 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[state=open]:bg-foreground/10 data-[state=open]:text-foreground"
|
||||
>
|
||||
<ChevronDown className="size-3.5" />
|
||||
</button>
|
||||
<ChevronDown className="text-muted-foreground" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" sideOffset={6} className="w-64">
|
||||
<DropdownMenuContent align="end" sideOffset={6} className="w-64">
|
||||
<QuickLaunchAgentMenuItems
|
||||
worktreeId={worktreeId}
|
||||
groupId={groupId}
|
||||
@@ -96,10 +93,7 @@ export function SplitPaneLandingAgentRow({
|
||||
/>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<span className="pointer-events-none ml-auto flex">
|
||||
<LandingRowShortcut shortcut={shortcut} />
|
||||
</span>
|
||||
</div>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { ShortcutKeyComboDetails } from '@/hooks/useShortcutLabel'
|
||||
import { ShortcutKeyCombo } from '../ShortcutKeyCombo'
|
||||
|
||||
export const LANDING_ROW_CLASS =
|
||||
'flex h-9 w-full items-center gap-3 rounded-md px-2.5 text-sm text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:outline-none'
|
||||
export function LandingRowLabel({ label }: { label: string }): React.JSX.Element {
|
||||
return <span className="min-w-0 flex-1 truncate text-left">{label}</span>
|
||||
}
|
||||
|
||||
export function LandingRowShortcut({
|
||||
shortcut
|
||||
|
||||
Reference in New Issue
Block a user