Unified · Split
| @@ -8,6 +8,7 @@ import type { · 7 unmodified lines | ||
| 8 | 8 | import { getTerminalLabel } from "@t3tools/shared/terminalLabels"; |
| 9 | 9 | import { |
| 10 | 10 | Bot, |
| 11 | + ChevronDown, | |
| 11 | 12 | FileDiff, |
| 12 | 13 | Files, |
| 13 | 14 | GitPullRequest, |
| @@ -283,6 +284,8 @@ function SurfaceMenuItem(props: { · 269 unmodified lines | ||
| 283 | 284 | */ |
| 284 | 285 | function RightPanelEmptyState(props: { |
| 285 | 286 | onAddBrowser: () => void; |
| 287 | + onAddBrowserInProfile: (profileId: string) => void; | |
| 288 | + browserProfiles: ReadonlyArray<{ readonly id: string; readonly name: string }>; | |
| 286 | 289 | onAddTerminal: () => void; |
| 287 | 290 | onAddDiff: () => void; |
| 288 | 291 | onAddFiles: () => void; |
| @@ -473,31 +476,78 @@ function RightPanelEmptyState(props: { · 184 unmodified lines | ||
| 473 | 476 | <div className="grid grid-cols-2 gap-2"> |
| 474 | 477 | {actions.map((action) => |
| 475 | 478 | action.available ? ( |
| 476 | − <button | |
| 479 | + // The card is itself a button, so the profile chooser sits beside | |
| 480 | + // it in a wrapper rather than inside it. Hover lives on the | |
| 481 | + // wrapper: the chooser overlays the card, and a pointer moving | |
| 482 | + // onto it must not read as leaving the card. | |
| 483 | + <div | |
| 477 | 484 | key={action.label} |
| 478 | − type="button" | |
| 479 | − onClick={action.onClick} | |
| 485 | + className="group relative" | |
| 480 | 486 | onMouseEnter={() => setHighlight(availableActions.indexOf(action))} |
| 481 | 487 | onMouseLeave={() => |
| 482 | 488 | setHighlight((current) => |
| 483 | 489 | current === availableActions.indexOf(action) ? -1 : current, |
| 484 | 490 | ) |
| 485 | 491 | } |
| 486 | − className={cn( | |
| 487 | − "relative flex w-full cursor-pointer flex-col items-start p-4 text-left transition hover:border-border hover:bg-accent/60", | |
| 488 | − cardShellClass, | |
| 489 | − isHighlighted(action) && highlightedCardClass, | |
| 490 | − )} | |
| 491 | 492 | > |
| 492 | − <Kbd className="absolute top-3 right-3">{action.shortcut}</Kbd> | |
| 493 | − <span className="flex items-center gap-2 pe-8"> | |
| 494 | − {actionIcon(action)} | |
| 495 | − <span className="font-medium text-sm">{action.label}</span> | |
| 496 | − </span> | |
| 497 | − <span className="mt-1.5 text-muted-foreground text-xs leading-relaxed"> | |
| 498 | − {action.description} | |
| 499 | − </span> | |
| 500 | − </button> | |
| 493 | + <button | |
| 494 | + type="button" | |
| 495 | + onClick={action.onClick} | |
| 496 | + className={cn( | |
| 497 | + // Full height: the wrapper is the grid item that stretches | |
| 498 | + // to the row, so the button must fill it to stay level with | |
| 499 | + // its neighbour and keep the chooser anchored inside. | |
| 500 | + "relative flex h-full w-full cursor-pointer flex-col items-start p-4 text-left transition group-hover:border-border group-hover:bg-accent/60", | |
| 501 | + cardShellClass, | |
| 502 | + isHighlighted(action) && highlightedCardClass, | |
| 503 | + )} | |
| 504 | + > | |
| 505 | + <Kbd className="absolute top-3 right-3">{action.shortcut}</Kbd> | |
| 506 | + <span className="flex items-center gap-2 pe-8"> | |
| 507 | + {actionIcon(action)} | |
| 508 | + <span className="font-medium text-sm">{action.label}</span> | |
| 509 | + </span> | |
| 510 | + <span className="mt-1.5 text-muted-foreground text-xs leading-relaxed"> | |
| 511 | + {action.description} | |
| 512 | + </span> | |
| 513 | + </button> | |
| 514 | + {/* | |
| 515 | + Same choice the tab bar's "+" menu offers: the card opens the | |
| 516 | + default profile, the chevron picks another. Only worth showing | |
| 517 | + once there is something to choose between. | |
| 518 | + */} | |
| 519 | + {action.label === "Browser" && props.browserProfiles.length > 1 ? ( | |
| 520 | + <Menu> | |
| 521 | + <MenuTrigger | |
| 522 | + render={ | |
| 523 | + <Button | |
| 524 | + aria-label="Open browser in a profile" | |
| 525 | + className="absolute right-3 bottom-3 [--control-icon-color:currentColor]" | |
| 526 | + size="icon-xs" | |
| 527 | + variant="ghost-muted" | |
| 528 | + /> | |
| 529 | + } | |
| 530 | + > | |
| 531 | + <ChevronDown className="size-3.5" /> | |
| 532 | + </MenuTrigger> | |
| 533 | + <MenuPopup | |
| 534 | + align="end" | |
| 535 | + side="bottom" | |
| 536 | + sideOffset={6} | |
| 537 | + className="min-w-40 max-w-56" | |
| 538 | + > | |
| 539 | + {props.browserProfiles.map((profile) => ( | |
| 540 | + <MenuItem | |
| 541 | + key={profile.id} | |
| 542 | + onClick={() => props.onAddBrowserInProfile(profile.id)} | |
| 543 | + > | |
| 544 | + <span className="min-w-0 truncate">{profile.name}</span> | |
| 545 | + </MenuItem> | |
| 546 | + ))} | |
| 547 | + </MenuPopup> | |
| 548 | + </Menu> | |
| 549 | + ) : null} | |
| 550 | + </div> | |
| 501 | 551 | ) : ( |
| 502 | 552 | <div |
| 503 | 553 | key={action.label} |
| @@ -1053,6 +1103,8 @@ export function RightPanelTabs(props: RightPanelTabsProps) { · 549 unmodified lines | ||
| 1053 | 1103 | {props.activeSurfaceId === null ? ( |
| 1054 | 1104 | <RightPanelEmptyState |
| 1055 | 1105 | onAddBrowser={props.onAddBrowser} |
| 1106 | + onAddBrowserInProfile={props.onAddBrowserInProfile} | |
| 1107 | + browserProfiles={browserProfiles} | |
| 1056 | 1108 | onAddTerminal={props.onAddTerminal} |
| 1057 | 1109 | onAddDiff={props.onAddDiff} |
| 1058 | 1110 | onAddFiles={props.onAddFiles} |