This commit is contained in:
Neil
2026-03-17 14:09:17 -07:00
parent a7b6df4b0a
commit c8a4222d93
3 changed files with 51 additions and 54 deletions
+38 -38
View File
@@ -19,12 +19,7 @@ import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger
} from '@/components/ui/context-menu'
import type { TerminalTab } from '../../../shared/types'
@@ -43,14 +38,16 @@ interface SortableTabProps {
}
const TAB_COLORS = [
{ label: 'None', value: 'none' },
{ label: 'None', value: null },
{ label: 'Blue', value: '#3b82f6' },
{ label: 'Purple', value: '#a855f7' },
{ label: 'Pink', value: '#ec4899' },
{ label: 'Red', value: '#ef4444' },
{ label: 'Orange', value: '#f97316' },
{ label: 'Yellow', value: '#eab308' },
{ label: 'Green', value: '#22c55e' },
{ label: 'Blue', value: '#3b82f6' },
{ label: 'Purple', value: '#a855f7' },
{ label: 'Pink', value: '#ec4899' }
{ label: 'Teal', value: '#14b8a6' },
{ label: 'Gray', value: '#9ca3af' }
]
function SortableTab({
@@ -103,6 +100,12 @@ function SortableTab({
>
<TerminalIcon className="w-3.5 h-3.5 mr-1.5 shrink-0 text-muted-foreground" />
<span className="truncate max-w-[130px] mr-1.5">{tab.customTitle ?? tab.title}</span>
{tab.color && (
<span
className="mr-1.5 size-2 rounded-full shrink-0"
style={{ backgroundColor: tab.color }}
/>
)}
<button
className={`flex items-center justify-center w-4 h-4 rounded-sm shrink-0 ${
isActive
@@ -117,12 +120,6 @@ function SortableTab({
>
<X className="w-3 h-3" />
</button>
{tab.color && (
<span
className="ml-1.5 size-2 rounded-full shrink-0"
style={{ backgroundColor: tab.color }}
/>
)}
</div>
</ContextMenuTrigger>
@@ -136,7 +133,7 @@ function SortableTab({
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem
onClick={() => {
onSelect={() => {
const next = window.prompt('Change tab title', tab.customTitle ?? tab.title)
if (next === null) return
const trimmed = next.trim()
@@ -145,29 +142,32 @@ function SortableTab({
>
Change Title
</ContextMenuItem>
<ContextMenuSub>
<ContextMenuSubTrigger>Assign Tab Color</ContextMenuSubTrigger>
<ContextMenuSubContent className="w-44">
<ContextMenuRadioGroup
value={tab.color ?? 'none'}
onValueChange={(value) => onSetTabColor(tab.id, value === 'none' ? null : value)}
>
{TAB_COLORS.map((color) => (
<ContextMenuRadioItem key={color.value} value={color.value} className="gap-2">
{color.value !== 'none' ? (
<span
className="inline-block size-2 rounded-full"
style={{ backgroundColor: color.value }}
/>
) : (
<span className="inline-block size-2 rounded-full bg-transparent border border-muted-foreground/40" />
<div className="px-2 pt-1.5 pb-1">
<div className="text-xs font-medium text-muted-foreground mb-1.5">Tab Color</div>
<div className="flex flex-wrap gap-2">
{TAB_COLORS.map((color) => {
const isSelected = tab.color === color.value
return (
<ContextMenuItem
key={color.label}
className={`relative h-4 w-4 min-w-4 p-0 rounded-full border ${
isSelected ? 'ring-1 ring-foreground/70 ring-offset-1 ring-offset-popover' : ''
} ${
color.value ? 'border-transparent' : 'border-muted-foreground/50 bg-transparent'
}`}
style={color.value ? { backgroundColor: color.value } : undefined}
onSelect={() => {
onSetTabColor(tab.id, color.value)
}}
>
{color.value === null && (
<span className="absolute block h-px w-3 rotate-45 bg-muted-foreground/80" />
)}
{color.label}
</ContextMenuRadioItem>
))}
</ContextMenuRadioGroup>
</ContextMenuSubContent>
</ContextMenuSub>
</ContextMenuItem>
)
})}
</div>
</div>
</ContextMenuContent>
</ContextMenu>
)
@@ -127,12 +127,6 @@ const WorktreeCard = React.memo(function WorktreeCard({
[worktree.id, setActiveWorktree]
)
// Memoize badge style to avoid new object each render
const badgeStyle = useMemo(
() => (repo ? { backgroundColor: repo.badgeColor + '22', color: repo.badgeColor } : undefined),
[repo?.badgeColor]
)
return (
<WorktreeContextMenu worktree={worktree}>
<div
@@ -162,14 +156,13 @@ const WorktreeCard = React.memo(function WorktreeCard({
{repo && (
<Badge
variant="dot"
className="h-4 px-1.5 text-[9px] font-medium rounded-sm shrink-0"
style={badgeStyle}
className="h-[18px] px-1.5 text-[10px] font-semibold font-mono rounded-[3px] shrink-0"
>
<RepoDotLabel
name={repo.displayName}
color={repo.badgeColor}
className="max-w-[9rem]"
dotClassName="size-1"
dotClassName="size-1.5"
/>
</Badge>
)}
@@ -185,9 +178,11 @@ const WorktreeCard = React.memo(function WorktreeCard({
{pr && (
<HoverCard openDelay={300}>
<HoverCardTrigger asChild>
<div className="flex items-center gap-1 min-w-0 cursor-default">
<span className="text-[10px] text-muted-foreground shrink-0">PR</span>
<span className="text-[10px] text-foreground/80 truncate">{pr.title}</span>
<div className="flex items-center justify-between gap-2 min-w-0 cursor-default">
<div className="flex items-center gap-1 min-w-0">
<span className="text-[10px] text-muted-foreground shrink-0">PR</span>
<span className="text-[10px] text-foreground/80 truncate">{pr.title}</span>
</div>
<Badge
variant="secondary"
className={cn(
@@ -228,9 +223,11 @@ const WorktreeCard = React.memo(function WorktreeCard({
{issue && (
<HoverCard openDelay={300}>
<HoverCardTrigger asChild>
<div className="flex items-center gap-1 min-w-0 cursor-default">
<span className="text-[10px] text-muted-foreground shrink-0">Issue</span>
<span className="text-[10px] text-foreground/80 truncate">{issue.title}</span>
<div className="flex items-center justify-between gap-2 min-w-0 cursor-default">
<div className="flex items-center gap-1 min-w-0">
<span className="text-[10px] text-muted-foreground shrink-0">Issue</span>
<span className="text-[10px] text-foreground/80 truncate">{issue.title}</span>
</div>
<Badge
variant="secondary"
className={cn(
+1 -1
View File
@@ -11,7 +11,7 @@ const badgeVariants = cva(
variant: {
default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
dot: 'bg-muted/50 text-foreground border-border/50',
dot: 'bg-background text-foreground border-border shadow-xs dark:bg-secondary dark:border-white/20',
destructive:
'bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90',
outline: