diff --git a/.claude/skills/config.json b/.claude/skills/config.json index 6e59330..ad2bb4b 100644 --- a/.claude/skills/config.json +++ b/.claude/skills/config.json @@ -1,6 +1,6 @@ { -"jianyingDraftPath": "/Users/lc/Movies/JianyingPro/User Data/Projects/com.lveditor.draft", - "capcutMateDir": "/Users/lc/capcut-mate", + "jianyingDraftPath": "C:/Users/45070/AppData/Local/JianyingPro/User Data/Projects/com.lveditor.draft", + "capcutMateDir": "C:/Users/45070/capcut-mate", "capcutMateApiBase": "http://capcut.muyetools.cn/openapi/capcut-mate/v1", "imgbbApiKey": "deprecated", "geminiApiBaseUrl": "https://yunwu.ai", diff --git a/web/client/package-lock.json b/web/client/package-lock.json index 461bc97..772cf23 100644 --- a/web/client/package-lock.json +++ b/web/client/package-lock.json @@ -12,14 +12,17 @@ "clsx": "^2.1.0", "lucide-react": "^0.460.0", "markdown-it": "^14.1.0", + "prismjs": "^1.30.0", "react": "^18.3.0", "react-dom": "^18.3.0", "react-router-dom": "^7.15.0", + "react-simple-code-editor": "^0.14.1", "tailwind-merge": "^2.6.0", "zustand": "^5.0.0" }, "devDependencies": { "@types/markdown-it": "^14.1.0", + "@types/prismjs": "^1.26.6", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", @@ -1246,6 +1249,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/prismjs": { + "version": "1.26.6", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", + "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", @@ -2336,6 +2346,15 @@ "dev": true, "license": "MIT" }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", @@ -2439,6 +2458,16 @@ "react-dom": ">=18" } }, + "node_modules/react-simple-code-editor": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz", + "integrity": "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/web/client/package.json b/web/client/package.json index f4a9109..ad5ad08 100644 --- a/web/client/package.json +++ b/web/client/package.json @@ -12,14 +12,17 @@ "clsx": "^2.1.0", "lucide-react": "^0.460.0", "markdown-it": "^14.1.0", + "prismjs": "^1.30.0", "react": "^18.3.0", "react-dom": "^18.3.0", "react-router-dom": "^7.15.0", + "react-simple-code-editor": "^0.14.1", "tailwind-merge": "^2.6.0", "zustand": "^5.0.0" }, "devDependencies": { "@types/markdown-it": "^14.1.0", + "@types/prismjs": "^1.26.6", "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", "@types/react-router-dom": "^5.3.3", diff --git a/web/client/src/components/chat/ChatInput.tsx b/web/client/src/components/chat/ChatInput.tsx index 4056b3a..8455b84 100644 --- a/web/client/src/components/chat/ChatInput.tsx +++ b/web/client/src/components/chat/ChatInput.tsx @@ -1,11 +1,34 @@ -import { useState, useRef } from 'react'; -import { Send } from 'lucide-react'; +import { useState, useRef, useEffect } from 'react'; +import { Send, Terminal, Image, Play, FileText } from 'lucide-react'; import { Button } from '@/components/ui/button'; +const SLASH_COMMANDS = [ + { cmd: '/run', desc: '执行 pipeline 阶段', icon: Play }, + { cmd: '/status', desc: '查看管线进度', icon: Terminal }, + { cmd: '/images', desc: '生成图片', icon: Image }, + { cmd: '/list', desc: '列出可用账号', icon: FileText }, + { cmd: '/help', desc: '显示帮助', icon: Terminal }, +]; + export function ChatInput({ onSend, disabled }: { onSend: (content: string) => void; disabled?: boolean }) { const [input, setInput] = useState(''); + const [showCmds, setShowCmds] = useState(false); + const [cmdIdx, setCmdIdx] = useState(0); const ref = useRef(null); + useEffect(() => { + if (input.startsWith('/')) { + setShowCmds(true); + setCmdIdx(0); + } else { + setShowCmds(false); + } + }, [input]); + + const matchingCmds = input.startsWith('/') + ? SLASH_COMMANDS.filter((c) => c.cmd.startsWith(input.split(' ')[0])) + : []; + const handleSend = () => { if (!input.trim()) return; onSend(input.trim()); @@ -14,6 +37,18 @@ export function ChatInput({ onSend, disabled }: { onSend: (content: string) => v }; const handleKeyDown = (e: React.KeyboardEvent) => { + if (showCmds && matchingCmds.length > 0) { + if (e.key === 'Tab' || e.key === 'Enter') { + e.preventDefault(); + const cmd = matchingCmds[cmdIdx % matchingCmds.length].cmd; + setInput(cmd + ' '); + setShowCmds(false); + return; + } + if (e.key === 'ArrowDown') { e.preventDefault(); setCmdIdx((i) => (i + 1) % matchingCmds.length); return; } + if (e.key === 'ArrowUp') { e.preventDefault(); setCmdIdx((i) => (i - 1 + matchingCmds.length) % matchingCmds.length); return; } + } + if (e.key === 'Escape') { setShowCmds(false); return; } if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); @@ -21,21 +56,43 @@ export function ChatInput({ onSend, disabled }: { onSend: (content: string) => v }; return ( -
-
-