diff --git a/src/browser/components/AddSectionButton/AddSectionButton.tsx b/src/browser/components/AddSectionButton/AddSectionButton.tsx index beff820652..7b993e7cfd 100644 --- a/src/browser/components/AddSectionButton/AddSectionButton.tsx +++ b/src/browser/components/AddSectionButton/AddSectionButton.tsx @@ -3,11 +3,17 @@ import { Plus } from "lucide-react"; // import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/Tooltip"; interface AddSectionButtonProps { - onCreateSection: (name: string) => void; + onCreateSection: (name: string) => Promise; } +const alignWithSectionCaretStyle: React.CSSProperties = { + borderLeftWidth: 3, + borderLeftColor: "transparent", +}; + export const AddSectionButton: React.FC = ({ onCreateSection }) => { const [isCreating, setIsCreating] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); const [name, setName] = useState(""); const inputRef = useRef(null); @@ -17,26 +23,62 @@ export const AddSectionButton: React.FC = ({ onCreateSect } }, [isCreating]); - const handleSubmit = () => { + const handleSubmit = async () => { + if (isSubmitting) { + return; + } + const trimmed = name.trim(); - if (trimmed) { - onCreateSection(trimmed); + if (!trimmed) { + setName(""); + setIsCreating(false); + return; + } + + setIsSubmitting(true); + try { + // Keep the input open until creation succeeds so backend/IPC failures do not + // look like they created a section successfully. + const didCreateSection = await onCreateSection(trimmed); + if (!didCreateSection) { + return; + } + setName(""); + setIsCreating(false); + } catch { + // The caller owns error presentation; keep the current draft visible so the + // user can retry instead of losing their typed section name. + } finally { + setIsSubmitting(false); } - setName(""); - setIsCreating(false); + }; + + const submitWithoutThrowing = () => { + handleSubmit().catch(() => undefined); }; if (isCreating) { return ( -
+
+
+ +
setName(e.target.value)} - onBlur={handleSubmit} + disabled={isSubmitting} + onBlur={submitWithoutThrowing} onKeyDown={(e) => { - if (e.key === "Enter") handleSubmit(); + if (e.key === "Enter") { + submitWithoutThrowing(); + } if (e.key === "Escape") { setName(""); setIsCreating(false); @@ -44,7 +86,7 @@ export const AddSectionButton: React.FC = ({ onCreateSect }} placeholder="Section name..." data-testid="add-section-input" - className="bg-background/50 text-foreground ml-6 min-w-0 flex-1 rounded border border-white/20 px-1.5 py-0.5 text-[11px] outline-none select-text" + className="bg-background/50 text-foreground min-w-0 flex-1 rounded border border-white/20 px-1.5 py-0.5 text-[11px] outline-none select-text" />
); @@ -54,9 +96,14 @@ export const AddSectionButton: React.FC = ({ onCreateSect ); diff --git a/src/browser/components/AgentListItem/AgentListItem.tsx b/src/browser/components/AgentListItem/AgentListItem.tsx index c02b474b80..83559aad6b 100644 --- a/src/browser/components/AgentListItem/AgentListItem.tsx +++ b/src/browser/components/AgentListItem/AgentListItem.tsx @@ -86,7 +86,6 @@ export interface AgentListItemProps extends AgentListItemBaseProps { variant?: "workspace"; metadata: FrontendWorkspaceMetadata; projectName: string; - subAgentConnectorLayout?: "default" | "task-group-member"; isArchiving?: boolean; /** True when deletion is in-flight (optimistic UI while backend removes). */ isRemoving?: boolean; @@ -121,26 +120,18 @@ const LIST_ITEM_BASE_CLASSES = "bg-surface-primary relative flex items-start gap-1.5 rounded-l-sm py-2 pr-1.5 select-none transition-all duration-150"; const HIDE_INLINE_ACTIONS_ON_MOBILE_TOUCH = - "[@media(max-width:768px)_and_(hover:none)_and_(pointer:coarse)]:invisible [@media(max-width:768px)_and_(hover:none)_and_(pointer:coarse)]:pointer-events-none"; + "[@media(max-width:768px)_and_(hover:none)_and_(pointer:coarse)]:hidden"; const SHOW_INLINE_ACTIONS_ON_WIDE_TOUCH = - "[@media(min-width:769px)_and_(hover:none)_and_(pointer:coarse)]:opacity-100"; + "[@media(min-width:769px)_and_(hover:none)_and_(pointer:coarse)]:pointer-events-auto [@media(min-width:769px)_and_(hover:none)_and_(pointer:coarse)]:opacity-100"; +// Dense sidebar icon buttons should not inherit the global 44px coarse-pointer +// minimum, otherwise hidden/inline actions still reserve row width and push the +// visible controls off-screen. +const COMPACT_SIDEBAR_ICON_BUTTON_CLASSES = "!min-h-0 !min-w-0"; /** Calculate left padding based on nesting depth */ function getItemPaddingLeft(depth?: number): number { const safeDepth = typeof depth === "number" && Number.isFinite(depth) ? Math.max(0, depth) : 0; - return 8 + Math.min(32, safeDepth) * 12; -} - -function getSubAgentConnectorLeft( - indentLeft: number, - layout: "default" | "task-group-member" -): number { - return layout === "task-group-member" ? indentLeft - 2 : indentLeft + 9; -} - -function getAncestorTrunkLeft(depth: number, layout: "default" | "task-group-member"): number { - const indentLeft = getItemPaddingLeft(depth); - return layout === "task-group-member" ? indentLeft + 6 : indentLeft + 8; + return 12 + Math.min(32, safeDepth) * 12; } type VisualState = "active" | "idle" | "seen" | "hidden" | "error" | "question"; @@ -257,7 +248,12 @@ function QuickArchiveButton(props: {
@@ -1876,7 +1886,7 @@ const ProjectSidebarInner: React.FC = ({ sortedProjectPaths.map((projectPath) => { const config = userProjects.get(projectPath); if (!config) return null; - const projectFolderColor = config.color + const projectColor = config.color ? resolveSectionColor(config.color) : undefined; const projectName = getProjectNameFromPath(projectPath); @@ -1889,13 +1899,12 @@ const ProjectSidebarInner: React.FC = ({ const isEditingProjectDisplayName = editingProjectPath === projectPath; const projectWorkspaces = singleProjectWorkspacesByProject.get(projectPath) ?? []; - const projectAgentCount = projectWorkspaces.length; const projectHasAttention = projectWorkspaces.some( (workspace) => workspaceAttentionById.get(workspace.id) === true ); return ( -
+
= ({ }} aria-label={`${isExpanded ? "Collapse" : "Expand"} project ${projectName}`} data-project-path={projectPath} - className="text-secondary hover:bg-hover hover:border-border-light mr-1.5 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 transition-all duration-200" + className={cn( + "text-secondary hover:bg-hover hover:border-border-light mr-1.5 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 transition-all duration-200", + COMPACT_PROJECT_ICON_BUTTON_CLASSES + )} > - {/* Mobile: nudge folder icon left so it visually centers above connector line. */} - - - {isExpanded ? ( - - ) : ( - - )} - +
= ({ }} /> ) : ( -
+
+ {projectColor && ( +
)} @@ -2047,7 +2037,10 @@ const ProjectSidebarInner: React.FC = ({ }} aria-label={`New chat in ${projectName}`} data-project-path={projectPath} - className="text-content-secondary hover:bg-hover hover:border-border-light flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent text-sm leading-none transition-all duration-200" + className={cn( + "text-content-secondary hover:bg-hover hover:border-border-light mr-1 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent text-sm leading-none transition-all duration-200", + COMPACT_PROJECT_ICON_BUTTON_CLASSES + )} > @@ -2065,7 +2058,10 @@ const ProjectSidebarInner: React.FC = ({ }} aria-label={`Project options for ${projectName}`} data-project-path={projectPath} - className="text-content-secondary hover:bg-hover hover:border-border-light flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent transition-all duration-200" + className={cn( + "text-content-secondary hover:bg-hover hover:border-border-light flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent transition-all duration-200", + COMPACT_PROJECT_ICON_BUTTON_CLASSES + )} > @@ -2079,17 +2075,8 @@ const ProjectSidebarInner: React.FC = ({ id={workspaceListId} role="region" aria-label={`Workspaces for ${projectName}`} - className="relative pt-1" + className="pt-1" > -