| ... | @@ -45,12 +45,15 @@ import { | ... | @@ -45,12 +45,15 @@ import { |
| 45 | CircleCheckIcon, | 45 | CircleCheckIcon, |
| 46 | CircleDashedIcon, | 46 | CircleDashedIcon, |
| 47 | ClockIcon, | 47 | ClockIcon, |
| | 48 | EyeIcon, |
| 48 | FolderIcon, | 49 | FolderIcon, |
| 49 | GitBranchIcon, | 50 | GitBranchIcon, |
| | 51 | MessageCircleQuestionIcon, |
| 50 | PinIcon, | 52 | PinIcon, |
| 51 | PinOffIcon, | 53 | PinOffIcon, |
| 52 | PlusIcon, | 54 | PlusIcon, |
| 53 | SettingsIcon, | 55 | SettingsIcon, |
| | 56 | ShieldQuestionIcon, |
| 54 | SquarePenIcon, | 57 | SquarePenIcon, |
| 55 | TerminalIcon, | 58 | TerminalIcon, |
| 56 | Undo2Icon, | 59 | Undo2Icon, |
| ... | @@ -1088,8 +1091,6 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1088,8 +1091,6 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1088 | // switching sidebars must not light up every historical thread as unread. | 1091 | // switching sidebars must not light up every historical thread as unread. |
| 1089 | const isUnread = hasUnseenCompletion({ ...thread, lastVisitedAt }); | 1092 | const isUnread = hasUnseenCompletion({ ...thread, lastVisitedAt }); |
| 1090 | const status = resolveSidebarThreadStatus(thread); | 1093 | const status = resolveSidebarThreadStatus(thread); |
| 1091 | const isInFlight = | | |
| 1092 | status === "working" || status === "monitoring" || status === "approval" || status === "input"; | | |
| 1093 | // A woken thread reappears at its original position (the sort is | 1094 | // A woken thread reappears at its original position (the sort is |
| 1094 | // deliberately static), so the pill has to carry the weight. Snoozing is | 1095 | // deliberately static), so the pill has to carry the weight. Snoozing is |
| 1095 | // an explicit act, so the pill clears only when the user re-engages: | 1096 | // an explicit act, so the pill clears only when the user re-engages: |
| ... | @@ -1123,35 +1124,32 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1123,35 +1124,32 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1123 | icon: "working" as const, | 1124 | icon: "working" as const, |
| 1124 | // No shimmer: a label that animates forever is noise in a sidebar | 1125 | // No shimmer: a label that animates forever is noise in a sidebar |
| 1125 | // full of them (and repaints every vsync on high-refresh displays). | 1126 | // full of them (and repaints every vsync on high-refresh displays). |
| 1126 | // Working is a background state, so it rests at the dim end of what | 1127 | className: "text-sky-600 dark:text-sky-400", |
| 1127 | // the old pulse cycled through; only the thread you have open gets | | |
| 1128 | // the label at full strength. | | |
| 1129 | className: cn("text-sky-600 dark:text-sky-400", !props.isActive && "opacity-75"), | | |
| 1130 | } | 1128 | } |
| 1131 | : status === "monitoring" | 1129 | : status === "monitoring" |
| 1132 | ? { | 1130 | ? { |
| 1133 | // Monitoring is calm background presence, not active progress | 1131 | // Monitoring is calm background presence, not active progress |
| 1134 | // (monitoring-pill D6), so it keeps the label at full strength. | 1132 | // (monitoring-pill D6), so it keeps the label at full strength. |
| 1135 | label: "Monitoring", | 1133 | label: "Monitoring", |
| 1136 | icon: null, | 1134 | icon: "monitoring" as const, |
| 1137 | className: "text-sky-600 dark:text-sky-400", | 1135 | className: "text-foreground dark:text-white", |
| 1138 | } | 1136 | } |
| 1139 | : status === "approval" | 1137 | : status === "approval" |
| 1140 | ? { | 1138 | ? { |
| 1141 | label: "Approval", | 1139 | label: "Approval", |
| 1142 | icon: null, | 1140 | icon: "approval" as const, |
| 1143 | className: "text-amber-700 dark:text-amber-300", | 1141 | className: "text-amber-700 dark:text-amber-300", |
| 1144 | } | 1142 | } |
| 1145 | : status === "input" | 1143 | : status === "input" |
| 1146 | ? { | 1144 | ? { |
| 1147 | label: "Input", | 1145 | label: "Input", |
| 1148 | icon: null, | 1146 | icon: "input" as const, |
| 1149 | className: "text-indigo-600 dark:text-indigo-300", | 1147 | className: "text-indigo-600 dark:text-indigo-300", |
| 1150 | } | 1148 | } |
| 1151 | : status === "failed" | 1149 | : status === "failed" |
| 1152 | ? { | 1150 | ? { |
| 1153 | label: "Failed", | 1151 | label: "Failed", |
| 1154 | icon: null, | 1152 | icon: "failed" as const, |
| 1155 | className: "text-red-700 dark:text-red-300", | 1153 | className: "text-red-700 dark:text-red-300", |
| 1156 | } | 1154 | } |
| 1157 | : isWoke | 1155 | : isWoke |
| ... | @@ -1396,10 +1394,6 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1396,10 +1394,6 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1396 | : shouldRecede | 1394 | : shouldRecede |
| 1397 | ? "text-sidebar-muted-foreground/75 hover:bg-sidebar-row-hover hover:text-sidebar-foreground" | 1395 | ? "text-sidebar-muted-foreground/75 hover:bg-sidebar-row-hover hover:text-sidebar-foreground" |
| 1398 | : "bg-transparent text-sidebar-foreground hover:bg-sidebar-row-hover", | 1396 | : "bg-transparent text-sidebar-foreground hover:bg-sidebar-row-hover", |
| 1399 | isInFlight && | | |
| 1400 | !props.isActive && | | |
| 1401 | !isSelected && | | |
| 1402 | "opacity-70 transition-opacity hover:opacity-100", | | |
| 1403 | isFileDragOver && "ring-1 ring-inset ring-primary/70", | 1397 | isFileDragOver && "ring-1 ring-inset ring-primary/70", |
| 1404 | // The hover tint must not clobber an active/selected row's own surface. | 1398 | // The hover tint must not clobber an active/selected row's own surface. |
| 1405 | isFileDragOver && !props.isActive && !isSelected && "bg-sidebar-row-hover", | 1399 | isFileDragOver && !props.isActive && !isSelected && "bg-sidebar-row-hover", |
| ... | @@ -1462,7 +1456,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1462,7 +1456,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1462 | "truncate", | 1456 | "truncate", |
| 1463 | shouldRecede | 1457 | shouldRecede |
| 1464 | ? "text-secondary-label" | 1458 | ? "text-secondary-label" |
| 1465 | : isUnread || isWoke | 1459 | : isUnread || isWoke || status === "input" |
| 1466 | ? "text-foreground" | 1460 | ? "text-foreground" |
| 1467 | : status === "failed" | 1461 | : status === "failed" |
| 1468 | ? "text-foreground/95" | 1462 | ? "text-foreground/95" |
| ... | @@ -1472,7 +1466,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1472,7 +1466,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1472 | "truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground", | 1466 | "truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground", |
| 1473 | shouldRecede | 1467 | shouldRecede |
| 1474 | ? "text-secondary-label/70" | 1468 | ? "text-secondary-label/70" |
| 1475 | : props.isActive || isWoke | 1469 | : props.isActive || isWoke || status === "input" |
| 1476 | ? "text-foreground" | 1470 | ? "text-foreground" |
| 1477 | : isUnread | 1471 | : isUnread |
| 1478 | ? "text-muted-foreground" | 1472 | ? "text-muted-foreground" |
| ... | @@ -1814,6 +1808,14 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1814,6 +1808,14 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1814 | > | 1808 | > |
| 1815 | {topStatus.icon === "working" ? ( | 1809 | {topStatus.icon === "working" ? ( |
| 1816 | <CircleDashedIcon aria-hidden className="size-4 shrink-0" /> | 1810 | <CircleDashedIcon aria-hidden className="size-4 shrink-0" /> |
| | 1811 | ) : topStatus.icon === "input" ? ( |
| | 1812 | <MessageCircleQuestionIcon aria-hidden className="size-4 shrink-0" /> |
| | 1813 | ) : topStatus.icon === "approval" ? ( |
| | 1814 | <ShieldQuestionIcon aria-hidden className="size-4 shrink-0" /> |
| | 1815 | ) : topStatus.icon === "failed" ? ( |
| | 1816 | <CircleAlertIcon aria-hidden className="size-4 shrink-0" /> |
| | 1817 | ) : topStatus.icon === "monitoring" ? ( |
| | 1818 | <EyeIcon aria-hidden className="size-4 shrink-0" /> |
| 1817 | ) : topStatus.icon === "done" ? ( | 1819 | ) : topStatus.icon === "done" ? ( |
| 1818 | <CircleCheckIcon aria-hidden className="size-4 shrink-0" /> | 1820 | <CircleCheckIcon aria-hidden className="size-4 shrink-0" /> |
| 1819 | ) : null} | 1821 | ) : null} |