From 994ee488b91b8858f4d5ec7ab57e22da3cd882d5 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 2 Apr 2026 22:53:52 +0700 Subject: [PATCH] Increase workspace status emphasis and use amber alert for needs input --- .../src/components/sidebar-workspace-list.tsx | 53 +++++++++++++++++-- .../workspace/workspace-tab-presentation.tsx | 26 +++++++-- packages/app/src/utils/status-dot-color.ts | 6 +++ 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/packages/app/src/components/sidebar-workspace-list.tsx b/packages/app/src/components/sidebar-workspace-list.tsx index 1e2727387..9333fbf46 100644 --- a/packages/app/src/components/sidebar-workspace-list.tsx +++ b/packages/app/src/components/sidebar-workspace-list.tsx @@ -27,6 +27,7 @@ import { type GestureType } from "react-native-gesture-handler"; import * as Clipboard from "expo-clipboard"; import { Archive, + CircleAlert, ChevronDown, ChevronRight, Copy, @@ -73,7 +74,7 @@ import { decideLongPressMove } from "@/utils/sidebar-gesture-arbitration"; import { confirmDialog } from "@/utils/confirm-dialog"; import { projectIconPlaceholderLabelFromDisplayName } from "@/utils/project-display-name"; import { shouldRenderSyncedStatusLoader } from "@/utils/status-loader"; -import { getStatusDotColor } from "@/utils/status-dot-color"; +import { getStatusDotColor, isEmphasizedStatusDotBucket } from "@/utils/status-dot-color"; import { Button } from "@/components/ui/button"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Shortcut } from "@/components/ui/shortcut"; @@ -100,6 +101,10 @@ const workspaceKeyExtractor = (workspace: SidebarWorkspaceEntry) => workspace.wo const projectKeyExtractor = (project: SidebarProjectEntry) => project.projectKey; const EMPTY_WORKSPACES = new Map(); const WORKSPACE_STATUS_DOT_WIDTH = 14; +const DEFAULT_STATUS_DOT_SIZE = 7; +const EMPHASIZED_STATUS_DOT_SIZE = 9; +const DEFAULT_STATUS_DOT_OFFSET = 0; +const EMPHASIZED_STATUS_DOT_OFFSET = -1; const GITHUB_PR_STATE_LABELS: Record = { open: "Open", merged: "Merged", @@ -238,6 +243,14 @@ function WorkspaceStatusIndicator({ ); } + if (bucket === "needs_input") { + return ( + + + + ); + } + const KindIcon = workspaceKind === "local_checkout" ? Monitor @@ -247,6 +260,13 @@ function WorkspaceStatusIndicator({ if (!KindIcon) return null; const dotColor = getStatusDotColor({ theme, bucket, showDoneAsInactive: false }); + const statusDotSize = isEmphasizedStatusDotBucket(bucket) + ? EMPHASIZED_STATUS_DOT_SIZE + : DEFAULT_STATUS_DOT_SIZE; + const statusDotOffset = + statusDotSize === EMPHASIZED_STATUS_DOT_SIZE + ? EMPHASIZED_STATUS_DOT_OFFSET + : DEFAULT_STATUS_DOT_OFFSET; return ( @@ -258,6 +278,10 @@ function WorkspaceStatusIndicator({ { backgroundColor: dotColor, borderColor: theme.colors.surface0, + width: statusDotSize, + height: statusDotSize, + right: statusDotOffset, + bottom: statusDotOffset, }, ]} /> @@ -327,11 +351,26 @@ function ProjectLeadingVisual({ ); } + if (activeWorkspace.statusBucket === "needs_input") { + return ( + + + + ); + } + const dotColor = getStatusDotColor({ theme, bucket: activeWorkspace.statusBucket, showDoneAsInactive: false, }); + const statusDotSize = isEmphasizedStatusDotBucket(activeWorkspace.statusBucket) + ? EMPHASIZED_STATUS_DOT_SIZE + : DEFAULT_STATUS_DOT_SIZE; + const statusDotOffset = + statusDotSize === EMPHASIZED_STATUS_DOT_SIZE + ? EMPHASIZED_STATUS_DOT_OFFSET + : DEFAULT_STATUS_DOT_OFFSET; return ( @@ -343,6 +382,10 @@ function ProjectLeadingVisual({ { backgroundColor: dotColor, borderColor: theme.colors.surface0, + width: statusDotSize, + height: statusDotSize, + right: statusDotOffset, + bottom: statusDotOffset, }, ]} /> @@ -2184,10 +2227,10 @@ const styles = StyleSheet.create((theme) => ({ }, statusDotOverlay: { position: "absolute", - right: 0, - bottom: 0, - width: 7, - height: 7, + right: DEFAULT_STATUS_DOT_OFFSET, + bottom: DEFAULT_STATUS_DOT_OFFSET, + width: DEFAULT_STATUS_DOT_SIZE, + height: DEFAULT_STATUS_DOT_SIZE, borderRadius: theme.borderRadius.full, borderWidth: 1, }, diff --git a/packages/app/src/screens/workspace/workspace-tab-presentation.tsx b/packages/app/src/screens/workspace/workspace-tab-presentation.tsx index ba7c7dfa9..694639e14 100644 --- a/packages/app/src/screens/workspace/workspace-tab-presentation.tsx +++ b/packages/app/src/screens/workspace/workspace-tab-presentation.tsx @@ -8,7 +8,7 @@ import { ensurePanelsRegistered } from "@/panels/register-panels"; import { getPanelRegistration } from "@/panels/panel-registry"; import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types"; import type { SidebarStateBucket } from "@/utils/sidebar-agent-state"; -import { getStatusDotColor } from "@/utils/status-dot-color"; +import { getStatusDotColor, isEmphasizedStatusDotBucket } from "@/utils/status-dot-color"; import { shouldRenderSyncedStatusLoader } from "@/utils/status-loader"; export interface WorkspaceTabPresentation { @@ -21,6 +21,11 @@ export interface WorkspaceTabPresentation { statusBucket: SidebarStateBucket | null; } +const DEFAULT_STATUS_DOT_SIZE = 7; +const EMPHASIZED_STATUS_DOT_SIZE = 9; +const DEFAULT_STATUS_DOT_OFFSET = -2; +const EMPHASIZED_STATUS_DOT_OFFSET = -3; + type WorkspaceTabPresentationResolverProps = { tab: WorkspaceTabDescriptor; serverId: string; @@ -114,6 +119,13 @@ export function WorkspaceTabIcon({ bucket: presentation.statusBucket, showDoneAsInactive: false, }); + const statusDotSize = isEmphasizedStatusDotBucket(presentation.statusBucket) + ? EMPHASIZED_STATUS_DOT_SIZE + : DEFAULT_STATUS_DOT_SIZE; + const statusDotOffset = + statusDotSize === EMPHASIZED_STATUS_DOT_SIZE + ? EMPHASIZED_STATUS_DOT_OFFSET + : DEFAULT_STATUS_DOT_OFFSET; const shouldShowLoader = shouldRenderSyncedStatusLoader({ bucket: presentation.statusBucket, }); @@ -137,6 +149,10 @@ export function WorkspaceTabIcon({ { backgroundColor: statusDotColor, borderColor: statusDotBorderColor ?? theme.colors.surface0, + width: statusDotSize, + height: statusDotSize, + right: statusDotOffset, + bottom: statusDotOffset, }, ]} /> @@ -199,10 +215,10 @@ const styles = StyleSheet.create((theme) => ({ }, statusDot: { position: "absolute", - right: -2, - bottom: -2, - width: 7, - height: 7, + right: DEFAULT_STATUS_DOT_OFFSET, + bottom: DEFAULT_STATUS_DOT_OFFSET, + width: DEFAULT_STATUS_DOT_SIZE, + height: DEFAULT_STATUS_DOT_SIZE, borderRadius: theme.borderRadius.full, borderWidth: 1, }, diff --git a/packages/app/src/utils/status-dot-color.ts b/packages/app/src/utils/status-dot-color.ts index 22c24d4bd..2adcc2424 100644 --- a/packages/app/src/utils/status-dot-color.ts +++ b/packages/app/src/utils/status-dot-color.ts @@ -25,3 +25,9 @@ export function getStatusDotColor(input: { } return null; } + +export function isEmphasizedStatusDotBucket( + bucket: SidebarStateBucket | null | undefined, +): boolean { + return bucket === "needs_input" || bucket === "attention"; +}