From 3f840fff88f6c75cf0ef3f456c2d46bd5fe2d2d1 Mon Sep 17 00:00:00 2001 From: Douglas Date: Fri, 24 Apr 2026 00:17:41 +0100 Subject: [PATCH] feat(app): update workspace shell, nav, and integrations Refresh IntegrationList, session rows, TopBar, and recent sessions. Remove recent-session delete from Workspace in favor of handling elsewhere. Made-with: Cursor --- .../Dashboard/IntegrationList/index.tsx | 83 ++++++++----- .../ProjectPageSidebar/NavListSessionRows.tsx | 112 ++++++++++-------- src/components/TopBar/index.tsx | 69 ++++++----- .../Workspace/WorkspaceRecentSessions.tsx | 4 +- src/components/Workspace/index.tsx | 14 --- 5 files changed, 146 insertions(+), 136 deletions(-) diff --git a/src/components/Dashboard/IntegrationList/index.tsx b/src/components/Dashboard/IntegrationList/index.tsx index 05356a04..baf644b9 100644 --- a/src/components/Dashboard/IntegrationList/index.tsx +++ b/src/components/Dashboard/IntegrationList/index.tsx @@ -14,10 +14,10 @@ import { fetchGet, fetchPost } from '@/api/http'; import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; import { Tooltip, TooltipContent, - TooltipSimple, TooltipTrigger, } from '@/components/ui/tooltip'; import { CircleAlert, Settings2 } from 'lucide-react'; @@ -36,6 +36,7 @@ import { } from '@/hooks/useIntegrationManagement'; import { getProxyBaseURL } from '@/lib'; import { OAuth } from '@/lib/oauth'; +import { cn } from '@/lib/utils'; import { MCPEnvDialog } from '@/pages/Connectors/components/MCPEnvDialog'; import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -65,6 +66,11 @@ interface IntegrationListProps { installedKeys?: string[]; oauth?: OAuth | null; translationNamespace?: 'layout' | 'setting'; // For translation keys + className?: string; + /** Select mode: show a leading checkbox; use isIntegrationSelected + onToggleIntegration */ + selectWithCheckbox?: boolean; + isIntegrationSelected?: (item: IntegrationItem) => boolean; + onToggleIntegration?: (item: IntegrationItem, selected: boolean) => void; } export default function IntegrationList({ @@ -84,6 +90,10 @@ export default function IntegrationList({ installedKeys: _installedKeys = [], oauth: _oauth, translationNamespace = variant === 'select' ? 'layout' : 'setting', + className: rootClassName, + selectWithCheckbox = false, + isIntegrationSelected, + onToggleIntegration, }: IntegrationListProps) { const { t } = useTranslation(); const [showEnvConfig, setShowEnvConfig] = useState(false); @@ -294,15 +304,15 @@ export default function IntegrationList({ : 'flex flex-col w-full items-start justify-start gap-4'; const itemClassName = isSelectMode - ? 'cursor-pointer hover:bg-ds-bg-neutral-default-hover px-3 py-2 flex justify-between' + ? 'cursor-pointer gap-2 rounded-lg bg-ds-bg-neutral-subtle-default px-3 py-2 min-h-0 flex w-full items-center justify-between hover:bg-ds-bg-neutral-default-hover' : 'w-full px-6 py-4 bg-ds-bg-neutral-subtle-default rounded-2xl'; const titleClassName = isSelectMode - ? 'text-base leading-snug font-bold text-ds-text-brand-default-default' + ? 'min-w-0 flex-1 text-sm font-bold leading-5 text-ds-text-neutral-default-default sm:text-base line-clamp-2 break-words' : 'text-label-lg font-bold text-ds-text-neutral-default-default'; return ( -
+
{ const isInstalled = !!installed[item.key]; const isComingSoon = COMING_SOON_ITEMS.includes(item.name); + const envCount = item.env_vars?.length ?? 0; + const selectedForTool = + selectWithCheckbox && isIntegrationSelected + ? isIntegrationSelected(item) + : false; + const checkboxDisabled = isComingSoon || (!isInstalled && envCount > 0); return (
@@ -320,45 +336,48 @@ export default function IntegrationList({ onClick={ isSelectMode ? () => { - if (!isComingSoon) { - if (item.env_vars.length === 0 || isInstalled) { - // Ensure toolkit field is passed and normalized for known cases - const normalizedToolkit = - item.name === 'Notion' - ? 'notion_mcp_toolkit' - : item.toolkit; - addOption?.( - { ...item, toolkit: normalizedToolkit }, - true - ); + if (isComingSoon) return; + if (selectWithCheckbox && onToggleIntegration) { + if (isInstalled) { + onToggleIntegration(item, !selectedForTool); + } else if (envCount === 0) { + onToggleIntegration(item, !selectedForTool); } else { handleInstall(item); } + return; + } + if (envCount === 0 || isInstalled) { + const normalizedToolkit = + item.name === 'Notion' + ? 'notion_mcp_toolkit' + : item.toolkit; + addOption?.( + { ...item, toolkit: normalizedToolkit }, + true + ); + } else { + handleInstall(item); } } : undefined } > {isSelectMode ? ( -
- {(isSelectMode || showStatusDot) && ( - icon + {selectWithCheckbox && ( + { + if (c === 'indeterminate') return; + onToggleIntegration?.(item, c === true); }} + onClick={(e) => e.stopPropagation()} + aria-label={item.name} /> )} -
{item.name}
-
- - - -
+ {item.name}
) : (
@@ -433,7 +452,7 @@ export default function IntegrationList({
)} - {isSelectMode && item.env_vars.length !== 0 && ( + {isSelectMode && envCount !== 0 && ( - - - onDeleteSession?.(session.id)} - > - {deleteLabel} - - - -
+
+ + + + + + onDeleteSession?.(session.id)} + > + {deleteLabel} + + + +
+ + ) : null}
); diff --git a/src/components/TopBar/index.tsx b/src/components/TopBar/index.tsx index 207068fc..a8098fef 100644 --- a/src/components/TopBar/index.tsx +++ b/src/components/TopBar/index.tsx @@ -184,7 +184,7 @@ function HeaderWin() { return (
-
+
{isHistoryRoute ? ( ) : ( @@ -232,16 +230,14 @@ function HeaderWin() { > )} @@ -252,16 +248,14 @@ function HeaderWin() { >
@@ -335,9 +327,11 @@ function HeaderWin() { onClick={() => handleShare(chatStore.activeTaskId as string) } - variant="ghost" - size="icon" - className="no-drag bg-ds-bg-information-subtle-default !text-ds-text-information-strong-default rounded-full" + variant="secondary" + size="sm" + buttonContent="icon-only" + buttonRadius="full" + tone="information" aria-label={t('layout.share')} > @@ -352,14 +346,15 @@ function HeaderWin() { setReportBugOpen(true)} + buttonContent="icon-only" > - + gift-icon @@ -404,10 +402,11 @@ function HeaderWin() { diff --git a/src/components/Workspace/WorkspaceRecentSessions.tsx b/src/components/Workspace/WorkspaceRecentSessions.tsx index b24a24ea..2116c4b1 100644 --- a/src/components/Workspace/WorkspaceRecentSessions.tsx +++ b/src/components/Workspace/WorkspaceRecentSessions.tsx @@ -62,7 +62,6 @@ export interface WorkspaceRecentSessionsProps { activeTaskId: string | null; onSelectSession: (sessionId: string) => void; onOpenAllSessions: () => void; - onDeleteSession: (sessionId: string) => void; } /** @@ -74,7 +73,6 @@ export function WorkspaceRecentSessions({ activeTaskId, onSelectSession, onOpenAllSessions, - onDeleteSession, }: WorkspaceRecentSessionsProps) { const { t } = useTranslation(); @@ -135,10 +133,10 @@ export function WorkspaceRecentSessions({ sessions={sessions} activeSessionId={activeTaskId} onSessionClick={onSelectSession} - onDeleteSession={onDeleteSession} folded={false} maxItems={NAV_LIST_SESSIONS_RECENT_MAX} panelListHover + showRowMenu={false} />
diff --git a/src/components/Workspace/index.tsx b/src/components/Workspace/index.tsx index 173fb5e8..4d7addfa 100644 --- a/src/components/Workspace/index.tsx +++ b/src/components/Workspace/index.tsx @@ -87,19 +87,6 @@ export default function Workspace() { useState(false); const textareaRef = useRef(null); - const handleWorkspaceRecentDeleteSession = useCallback( - (sessionId: string) => { - if (!chatStore) return; - if (!window.confirm(t('layout.delete-task-confirmation'))) return; - const wasActive = chatStore.activeTaskId === sessionId; - chatStore.removeTask(sessionId); - if (wasActive) { - setActiveWorkspaceTab('workforce'); - } - }, - [chatStore, setActiveWorkspaceTab, t] - ); - useEffect(() => { if (!workspaceWorkWithPanelOpen) return; const onKeyDown = (e: KeyboardEvent) => { @@ -427,7 +414,6 @@ export default function Workspace() { setActiveWorkspaceTab('session'); }} onOpenAllSessions={() => setActiveWorkspaceTab('sessions')} - onDeleteSession={handleWorkspaceRecentDeleteSession} /> )}