mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat(commands): add create button and file browser to commands page
- Add "New Command" button to CommandsManagerPage header - Integrate CommandCreateDialog for creating/importing commands - Add file browser button to source path input in CommandCreateDialog - Uses FloatingFileBrowser component for file selection - Supports browsing project directory for .md command files - Add i18n keys for browse file button (en/zh) Users can now create commands via: 1. Import existing .md file (with visual file picker) 2. AI-generate new command from description
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
XCircle,
|
||||
Loader2,
|
||||
Info,
|
||||
FolderOpen,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -31,6 +32,7 @@ import { Textarea } from '@/components/ui/Textarea';
|
||||
import { Label } from '@/components/ui/Label';
|
||||
import { validateCommandImport, createCommand } from '@/lib/api';
|
||||
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||
import { FloatingFileBrowser } from '@/components/terminal-dashboard/FloatingFileBrowser';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export interface CommandCreateDialogProps {
|
||||
@@ -68,6 +70,9 @@ export function CommandCreateDialog({ open, onOpenChange, onCreated, cliType = '
|
||||
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
|
||||
// File browser state
|
||||
const [isFileBrowserOpen, setIsFileBrowserOpen] = useState(false);
|
||||
|
||||
const resetState = useCallback(() => {
|
||||
setMode('import');
|
||||
setLocation('project');
|
||||
@@ -78,6 +83,7 @@ export function CommandCreateDialog({ open, onOpenChange, onCreated, cliType = '
|
||||
setCommandName('');
|
||||
setDescription('');
|
||||
setIsCreating(false);
|
||||
setIsFileBrowserOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleOpenChange = useCallback((open: boolean) => {
|
||||
@@ -250,16 +256,27 @@ export function CommandCreateDialog({ open, onOpenChange, onCreated, cliType = '
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="sourcePath">{formatMessage({ id: 'commands.create.sourcePath' })}</Label>
|
||||
<Input
|
||||
id="sourcePath"
|
||||
value={sourcePath}
|
||||
onChange={(e) => {
|
||||
setSourcePath(e.target.value);
|
||||
setValidationResult(null);
|
||||
}}
|
||||
placeholder={formatMessage({ id: 'commands.create.sourcePathPlaceholder' })}
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="sourcePath"
|
||||
value={sourcePath}
|
||||
onChange={(e) => {
|
||||
setSourcePath(e.target.value);
|
||||
setValidationResult(null);
|
||||
}}
|
||||
placeholder={formatMessage({ id: 'commands.create.sourcePathPlaceholder' })}
|
||||
className="font-mono text-sm flex-1"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => setIsFileBrowserOpen(true)}
|
||||
title={formatMessage({ id: 'commands.create.browseFile' })}
|
||||
>
|
||||
<FolderOpen className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{formatMessage({ id: 'commands.create.sourcePathHint' })}</p>
|
||||
</div>
|
||||
|
||||
@@ -401,6 +418,19 @@ export function CommandCreateDialog({ open, onOpenChange, onCreated, cliType = '
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
{/* File Browser for selecting source file */}
|
||||
<FloatingFileBrowser
|
||||
isOpen={isFileBrowserOpen}
|
||||
onClose={() => setIsFileBrowserOpen(false)}
|
||||
rootPath={projectPath}
|
||||
onInsertPath={(path) => {
|
||||
setSourcePath(path);
|
||||
setValidationResult(null);
|
||||
setIsFileBrowserOpen(false);
|
||||
}}
|
||||
initialSelectedPath={sourcePath || null}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"sourcePath": "Source File Path",
|
||||
"sourcePathPlaceholder": "Enter absolute path to command file",
|
||||
"sourcePathHint": "File must be a valid command markdown file",
|
||||
"browseFile": "Browse files",
|
||||
"customName": "Custom Name",
|
||||
"customNamePlaceholder": "Leave empty to use original name",
|
||||
"customNameHint": "Optional, overrides default command name",
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"sourcePath": "源文件路径",
|
||||
"sourcePathPlaceholder": "输入命令文件的绝对路径",
|
||||
"sourcePathHint": "文件必须是有效的命令 Markdown 文件",
|
||||
"browseFile": "浏览文件",
|
||||
"customName": "自定义名称",
|
||||
"customNamePlaceholder": "留空则使用原始名称",
|
||||
"customNameHint": "可选,覆盖默认命令名称",
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
AlertCircle,
|
||||
Maximize2,
|
||||
Minimize2,
|
||||
Plus,
|
||||
} from 'lucide-react';
|
||||
import { useAppStore, selectIsImmersiveMode } from '@/stores/appStore';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
@@ -27,6 +28,7 @@ import { Badge } from '@/components/ui/Badge';
|
||||
import { TabsNavigation } from '@/components/ui/TabsNavigation';
|
||||
import { useCommands, useCommandMutations } from '@/hooks';
|
||||
import { CommandGroupAccordion } from '@/components/commands/CommandGroupAccordion';
|
||||
import { CommandCreateDialog } from '@/components/shared/CommandCreateDialog';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ========== Main Page Component ==========
|
||||
@@ -42,6 +44,8 @@ export function CommandsManagerPage() {
|
||||
const [expandedGroups, setExpandedGroups] = useState<Set<string>>(new Set(['cli', 'workflow']));
|
||||
// Search state
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
// Create dialog state
|
||||
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
|
||||
|
||||
// Immersive mode state
|
||||
const isImmersiveMode = useAppStore(selectIsImmersiveMode);
|
||||
@@ -117,6 +121,10 @@ export function CommandsManagerPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button onClick={() => setIsCreateDialogOpen(true)} disabled={isToggling}>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
{formatMessage({ id: 'commands.actions.create' })}
|
||||
</Button>
|
||||
<button
|
||||
onClick={toggleImmersiveMode}
|
||||
className={cn(
|
||||
@@ -279,6 +287,16 @@ export function CommandsManagerPage() {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Command Create Dialog */}
|
||||
<CommandCreateDialog
|
||||
open={isCreateDialogOpen}
|
||||
onOpenChange={setIsCreateDialogOpen}
|
||||
onCreated={() => {
|
||||
setIsCreateDialogOpen(false);
|
||||
refetch();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user