mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
refactor(terminal-dashboard): simplify CLI launch to dialog-only mode
- Replace dropdown menu with direct dialog button - Remove unused state variables (selectedTool, launchMode, selectedShell) - Update button icon to Plus and label to "New Session" - Clean up i18n keys (remove unused tool/mode/shell options)
This commit is contained in:
@@ -16,29 +16,15 @@ import {
|
|||||||
Columns2,
|
Columns2,
|
||||||
Rows2,
|
Rows2,
|
||||||
Square,
|
Square,
|
||||||
Terminal,
|
|
||||||
ChevronDown,
|
|
||||||
Zap,
|
|
||||||
Settings,
|
|
||||||
Loader2,
|
Loader2,
|
||||||
Folder,
|
Folder,
|
||||||
Maximize2,
|
Maximize2,
|
||||||
Minimize2,
|
Minimize2,
|
||||||
|
Activity,
|
||||||
|
Plus,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuRadioGroup,
|
|
||||||
DropdownMenuRadioItem,
|
|
||||||
DropdownMenuSub,
|
|
||||||
DropdownMenuSubContent,
|
|
||||||
DropdownMenuSubTrigger,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
} from '@/components/ui/Dropdown';
|
|
||||||
import {
|
import {
|
||||||
useIssueQueueIntegrationStore,
|
useIssueQueueIntegrationStore,
|
||||||
selectAssociationChain,
|
selectAssociationChain,
|
||||||
@@ -47,11 +33,12 @@ import { useIssues, useIssueQueue } from '@/hooks/useIssues';
|
|||||||
import { useTerminalGridStore, selectTerminalGridFocusedPaneId } from '@/stores/terminalGridStore';
|
import { useTerminalGridStore, selectTerminalGridFocusedPaneId } from '@/stores/terminalGridStore';
|
||||||
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||||
import { toast } from '@/stores/notificationStore';
|
import { toast } from '@/stores/notificationStore';
|
||||||
|
import { useExecutionMonitorStore, selectActiveExecutionCount } from '@/stores/executionMonitorStore';
|
||||||
import { CliConfigModal, type CliSessionConfig } from './CliConfigModal';
|
import { CliConfigModal, type CliSessionConfig } from './CliConfigModal';
|
||||||
|
|
||||||
// ========== Types ==========
|
// ========== Types ==========
|
||||||
|
|
||||||
export type PanelId = 'issues' | 'queue' | 'inspector';
|
export type PanelId = 'issues' | 'queue' | 'inspector' | 'execution';
|
||||||
|
|
||||||
interface DashboardToolbarProps {
|
interface DashboardToolbarProps {
|
||||||
activePanel: PanelId | null;
|
activePanel: PanelId | null;
|
||||||
@@ -79,12 +66,6 @@ const LAYOUT_PRESETS = [
|
|||||||
{ id: 'grid-2x2' as const, icon: LayoutGrid, labelId: 'terminalDashboard.toolbar.layoutGrid' },
|
{ id: 'grid-2x2' as const, icon: LayoutGrid, labelId: 'terminalDashboard.toolbar.layoutGrid' },
|
||||||
];
|
];
|
||||||
|
|
||||||
type LaunchMode = 'default' | 'yolo';
|
|
||||||
type ShellKind = 'bash' | 'pwsh' | 'cmd';
|
|
||||||
|
|
||||||
const CLI_TOOLS = ['claude', 'gemini', 'qwen', 'codex', 'opencode'] as const;
|
|
||||||
type CliTool = (typeof CLI_TOOLS)[number];
|
|
||||||
|
|
||||||
// ========== Component ==========
|
// ========== Component ==========
|
||||||
|
|
||||||
export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen, onToggleFileSidebar, isSessionSidebarOpen, onToggleSessionSidebar, isFullscreen, onToggleFullscreen }: DashboardToolbarProps) {
|
export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen, onToggleFileSidebar, isSessionSidebarOpen, onToggleSessionSidebar, isFullscreen, onToggleFullscreen }: DashboardToolbarProps) {
|
||||||
@@ -109,6 +90,9 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
|||||||
const associationChain = useIssueQueueIntegrationStore(selectAssociationChain);
|
const associationChain = useIssueQueueIntegrationStore(selectAssociationChain);
|
||||||
const hasChain = associationChain !== null;
|
const hasChain = associationChain !== null;
|
||||||
|
|
||||||
|
// Execution monitor count
|
||||||
|
const executionCount = useExecutionMonitorStore(selectActiveExecutionCount);
|
||||||
|
|
||||||
// Layout preset handler
|
// Layout preset handler
|
||||||
const resetLayout = useTerminalGridStore((s) => s.resetLayout);
|
const resetLayout = useTerminalGridStore((s) => s.resetLayout);
|
||||||
const handlePreset = useCallback(
|
const handlePreset = useCallback(
|
||||||
@@ -121,14 +105,8 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
|||||||
// Launch CLI handlers
|
// Launch CLI handlers
|
||||||
const projectPath = useWorkflowStore(selectProjectPath);
|
const projectPath = useWorkflowStore(selectProjectPath);
|
||||||
const focusedPaneId = useTerminalGridStore(selectTerminalGridFocusedPaneId);
|
const focusedPaneId = useTerminalGridStore(selectTerminalGridFocusedPaneId);
|
||||||
// panes available via: useTerminalGridStore((s) => s.panes)
|
|
||||||
const createSessionAndAssign = useTerminalGridStore((s) => s.createSessionAndAssign);
|
const createSessionAndAssign = useTerminalGridStore((s) => s.createSessionAndAssign);
|
||||||
const [isCreating, setIsCreating] = useState(false);
|
const [isCreating, setIsCreating] = useState(false);
|
||||||
const [selectedTool, setSelectedTool] = useState<CliTool>('gemini');
|
|
||||||
const [launchMode, setLaunchMode] = useState<LaunchMode>('yolo');
|
|
||||||
const [selectedShell, setSelectedShell] = useState<ShellKind>(
|
|
||||||
typeof navigator !== 'undefined' && navigator.platform.toLowerCase().includes('win') ? 'cmd' : 'bash'
|
|
||||||
);
|
|
||||||
const [isConfigOpen, setIsConfigOpen] = useState(false);
|
const [isConfigOpen, setIsConfigOpen] = useState(false);
|
||||||
|
|
||||||
// Helper to get or create a focused pane
|
// Helper to get or create a focused pane
|
||||||
@@ -140,39 +118,12 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
|||||||
return useTerminalGridStore.getState().focusedPaneId;
|
return useTerminalGridStore.getState().focusedPaneId;
|
||||||
}, [focusedPaneId]);
|
}, [focusedPaneId]);
|
||||||
|
|
||||||
const handleQuickCreate = useCallback(async () => {
|
// Open config modal
|
||||||
if (!projectPath) return;
|
const handleOpenConfig = useCallback(() => {
|
||||||
setIsCreating(true);
|
|
||||||
try {
|
|
||||||
const targetPaneId = getOrCreateFocusedPane();
|
|
||||||
if (!targetPaneId) {
|
|
||||||
toast.error('无法创建会话', '未能获取或创建窗格');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await createSessionAndAssign(targetPaneId, {
|
|
||||||
workingDir: projectPath,
|
|
||||||
preferredShell: selectedShell,
|
|
||||||
tool: selectedTool,
|
|
||||||
launchMode,
|
|
||||||
}, projectPath);
|
|
||||||
} catch (error: unknown) {
|
|
||||||
// Handle both Error instances and ApiError-like objects
|
|
||||||
const message = error instanceof Error
|
|
||||||
? error.message
|
|
||||||
: (error as { message?: string })?.message
|
|
||||||
? (error as { message: string }).message
|
|
||||||
: String(error);
|
|
||||||
toast.error(`CLI 会话创建失败 (${selectedTool})`, message);
|
|
||||||
} finally {
|
|
||||||
setIsCreating(false);
|
|
||||||
}
|
|
||||||
}, [projectPath, createSessionAndAssign, selectedTool, selectedShell, launchMode, getOrCreateFocusedPane]);
|
|
||||||
|
|
||||||
const handleConfigure = useCallback(() => {
|
|
||||||
setIsConfigOpen(true);
|
setIsConfigOpen(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Create session from config modal
|
||||||
const handleCreateConfiguredSession = useCallback(async (config: CliSessionConfig) => {
|
const handleCreateConfiguredSession = useCallback(async (config: CliSessionConfig) => {
|
||||||
if (!projectPath) throw new Error('No project path');
|
if (!projectPath) throw new Error('No project path');
|
||||||
setIsCreating(true);
|
setIsCreating(true);
|
||||||
@@ -192,7 +143,6 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
|||||||
projectPath
|
projectPath
|
||||||
);
|
);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
// Handle both Error instances and ApiError-like objects
|
|
||||||
const message = error instanceof Error
|
const message = error instanceof Error
|
||||||
? error.message
|
? error.message
|
||||||
: (error as { message?: string })?.message
|
: (error as { message?: string })?.message
|
||||||
@@ -208,115 +158,24 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center gap-1 px-2 h-[40px] border-b border-border bg-muted/30 shrink-0">
|
<div className="flex items-center gap-1 px-2 h-[40px] border-b border-border bg-muted/30 shrink-0">
|
||||||
{/* Launch CLI dropdown */}
|
{/* Launch CLI button - opens config dialog */}
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<button
|
<button
|
||||||
|
onClick={handleOpenConfig}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs transition-colors',
|
'flex items-center gap-1.5 px-2.5 py-1.5 rounded-md text-xs transition-colors',
|
||||||
'text-muted-foreground hover:text-foreground hover:bg-muted',
|
'text-muted-foreground hover:text-foreground hover:bg-muted',
|
||||||
isCreating && 'opacity-50 cursor-wait'
|
isCreating && 'opacity-50 cursor-wait'
|
||||||
)}
|
)}
|
||||||
disabled={isCreating || !projectPath}
|
disabled={isCreating || !projectPath}
|
||||||
|
title={formatMessage({ id: 'terminalDashboard.toolbar.launchCliHint', defaultMessage: 'Click to configure and launch a CLI session' })}
|
||||||
>
|
>
|
||||||
{isCreating ? (
|
{isCreating ? (
|
||||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<Terminal className="w-3.5 h-3.5" />
|
<Plus className="w-3.5 h-3.5" />
|
||||||
)}
|
)}
|
||||||
<span>{formatMessage({ id: 'terminalDashboard.toolbar.launchCli' })}</span>
|
<span>{formatMessage({ id: 'terminalDashboard.toolbar.launchCli' })}</span>
|
||||||
<ChevronDown className="w-3 h-3" />
|
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="start" sideOffset={4}>
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger className="gap-2">
|
|
||||||
<span>{formatMessage({ id: 'terminalDashboard.toolbar.tool' })}</span>
|
|
||||||
<span className="text-xs text-muted-foreground">({selectedTool})</span>
|
|
||||||
</DropdownMenuSubTrigger>
|
|
||||||
<DropdownMenuSubContent>
|
|
||||||
<DropdownMenuRadioGroup
|
|
||||||
value={selectedTool}
|
|
||||||
onValueChange={(v) => setSelectedTool(v as CliTool)}
|
|
||||||
>
|
|
||||||
{CLI_TOOLS.map((tool) => (
|
|
||||||
<DropdownMenuRadioItem key={tool} value={tool}>
|
|
||||||
{tool}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
))}
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger className="gap-2">
|
|
||||||
<span>{formatMessage({ id: 'terminalDashboard.toolbar.mode' })}</span>
|
|
||||||
<span className="text-xs text-muted-foreground">
|
|
||||||
{launchMode === 'default'
|
|
||||||
? formatMessage({ id: 'terminalDashboard.toolbar.modeDefault' })
|
|
||||||
: formatMessage({ id: 'terminalDashboard.toolbar.modeYolo' })}
|
|
||||||
</span>
|
|
||||||
</DropdownMenuSubTrigger>
|
|
||||||
<DropdownMenuSubContent>
|
|
||||||
<DropdownMenuRadioGroup
|
|
||||||
value={launchMode}
|
|
||||||
onValueChange={(v) => setLaunchMode(v as LaunchMode)}
|
|
||||||
>
|
|
||||||
<DropdownMenuRadioItem value="default">
|
|
||||||
{formatMessage({ id: 'terminalDashboard.toolbar.modeDefault' })}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="yolo">
|
|
||||||
{formatMessage({ id: 'terminalDashboard.toolbar.modeYolo' })}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
|
|
||||||
<DropdownMenuSub>
|
|
||||||
<DropdownMenuSubTrigger className="gap-2">
|
|
||||||
<span>{formatMessage({ id: 'terminalDashboard.toolbar.shell' })}</span>
|
|
||||||
<span className="text-xs text-muted-foreground">
|
|
||||||
{selectedShell === 'cmd' ? 'cmd' : selectedShell === 'pwsh' ? 'pwsh' : 'bash'}
|
|
||||||
</span>
|
|
||||||
</DropdownMenuSubTrigger>
|
|
||||||
<DropdownMenuSubContent>
|
|
||||||
<DropdownMenuRadioGroup
|
|
||||||
value={selectedShell}
|
|
||||||
onValueChange={(v) => setSelectedShell(v as ShellKind)}
|
|
||||||
>
|
|
||||||
<DropdownMenuRadioItem value="cmd">
|
|
||||||
cmd {formatMessage({ id: 'terminalDashboard.toolbar.shellCmdDesc' })}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="bash">
|
|
||||||
bash (Git Bash/WSL)
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="pwsh">
|
|
||||||
pwsh (PowerShell)
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuSubContent>
|
|
||||||
</DropdownMenuSub>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={handleQuickCreate}
|
|
||||||
disabled={isCreating || !projectPath}
|
|
||||||
className="gap-2"
|
|
||||||
>
|
|
||||||
<Zap className="w-4 h-4" />
|
|
||||||
<span>{formatMessage({ id: 'terminalDashboard.toolbar.quickCreate' })}</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={handleConfigure}
|
|
||||||
disabled={isCreating || !projectPath}
|
|
||||||
className="gap-2"
|
|
||||||
>
|
|
||||||
<Settings className="w-4 h-4" />
|
|
||||||
<span>{formatMessage({ id: 'terminalDashboard.toolbar.configure' })}</span>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
|
|
||||||
{/* Separator */}
|
{/* Separator */}
|
||||||
<div className="w-px h-5 bg-border mx-1" />
|
<div className="w-px h-5 bg-border mx-1" />
|
||||||
@@ -354,6 +213,13 @@ export function DashboardToolbar({ activePanel, onTogglePanel, isFileSidebarOpen
|
|||||||
onClick={() => onTogglePanel('inspector')}
|
onClick={() => onTogglePanel('inspector')}
|
||||||
dot={hasChain}
|
dot={hasChain}
|
||||||
/>
|
/>
|
||||||
|
<ToolbarButton
|
||||||
|
icon={Activity}
|
||||||
|
label={formatMessage({ id: 'terminalDashboard.toolbar.executionMonitor', defaultMessage: 'Execution Monitor' })}
|
||||||
|
isActive={activePanel === 'execution'}
|
||||||
|
onClick={() => onTogglePanel('execution')}
|
||||||
|
badge={executionCount > 0 ? executionCount : undefined}
|
||||||
|
/>
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
icon={FolderOpen}
|
icon={FolderOpen}
|
||||||
label={formatMessage({ id: 'terminalDashboard.toolbar.files', defaultMessage: 'Files' })}
|
label={formatMessage({ id: 'terminalDashboard.toolbar.files', defaultMessage: 'Files' })}
|
||||||
|
|||||||
@@ -78,15 +78,8 @@
|
|||||||
"layoutSplitH": "Split Horizontal",
|
"layoutSplitH": "Split Horizontal",
|
||||||
"layoutSplitV": "Split Vertical",
|
"layoutSplitV": "Split Vertical",
|
||||||
"layoutGrid": "Grid 2x2",
|
"layoutGrid": "Grid 2x2",
|
||||||
"launchCli": "Launch CLI",
|
"launchCli": "New Session",
|
||||||
"tool": "Tool",
|
"launchCliHint": "Click to configure and create a new CLI session",
|
||||||
"mode": "Mode",
|
|
||||||
"modeDefault": "Default",
|
|
||||||
"modeYolo": "Yolo",
|
|
||||||
"shell": "Shell",
|
|
||||||
"shellCmdDesc": "(Recommended for Windows)",
|
|
||||||
"quickCreate": "Quick Create",
|
|
||||||
"configure": "Configure...",
|
|
||||||
"fullscreen": "Fullscreen",
|
"fullscreen": "Fullscreen",
|
||||||
"orchestrator": "Orchestrator"
|
"orchestrator": "Orchestrator"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -78,15 +78,8 @@
|
|||||||
"layoutSplitH": "左右分割",
|
"layoutSplitH": "左右分割",
|
||||||
"layoutSplitV": "上下分割",
|
"layoutSplitV": "上下分割",
|
||||||
"layoutGrid": "2x2 网格",
|
"layoutGrid": "2x2 网格",
|
||||||
"launchCli": "启动 CLI",
|
"launchCli": "新建会话",
|
||||||
"tool": "工具",
|
"launchCliHint": "点击配置并创建新的 CLI 会话",
|
||||||
"mode": "模式",
|
|
||||||
"modeDefault": "默认",
|
|
||||||
"modeYolo": "Yolo",
|
|
||||||
"shell": "Shell",
|
|
||||||
"shellCmdDesc": "(推荐 Windows)",
|
|
||||||
"quickCreate": "快速创建",
|
|
||||||
"configure": "配置...",
|
|
||||||
"fullscreen": "全屏",
|
"fullscreen": "全屏",
|
||||||
"orchestrator": "编排器"
|
"orchestrator": "编排器"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user