@winspan/claude-forge 8.35.0 → 8.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/core/ai/provider.d.ts +23 -1
  2. package/dist/core/ai/provider.d.ts.map +1 -1
  3. package/dist/core/ai/provider.js +67 -1
  4. package/dist/core/ai/provider.js.map +1 -1
  5. package/dist/core/ai/types.d.ts +28 -0
  6. package/dist/core/ai/types.d.ts.map +1 -1
  7. package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
  8. package/dist/daemon/handlers/user-prompt.js +25 -3
  9. package/dist/daemon/handlers/user-prompt.js.map +1 -1
  10. package/dist/daemon/index.d.ts.map +1 -1
  11. package/dist/daemon/index.js +6 -2
  12. package/dist/daemon/index.js.map +1 -1
  13. package/dist/engine/agent-router.d.ts +37 -0
  14. package/dist/engine/agent-router.d.ts.map +1 -1
  15. package/dist/engine/agent-router.js +58 -0
  16. package/dist/engine/agent-router.js.map +1 -1
  17. package/dist/engine/conventions/routing.yaml +31 -2
  18. package/dist/intelligence/classifier.d.ts +63 -43
  19. package/dist/intelligence/classifier.d.ts.map +1 -1
  20. package/dist/intelligence/classifier.js +256 -191
  21. package/dist/intelligence/classifier.js.map +1 -1
  22. package/dist/intelligence/context-gatherer.d.ts +101 -0
  23. package/dist/intelligence/context-gatherer.d.ts.map +1 -0
  24. package/dist/intelligence/context-gatherer.js +417 -0
  25. package/dist/intelligence/context-gatherer.js.map +1 -0
  26. package/dist/intelligence/cot-classifier.d.ts +95 -0
  27. package/dist/intelligence/cot-classifier.d.ts.map +1 -0
  28. package/dist/intelligence/cot-classifier.js +391 -0
  29. package/dist/intelligence/cot-classifier.js.map +1 -0
  30. package/dist/intelligence/execution-doc-builder.d.ts +90 -0
  31. package/dist/intelligence/execution-doc-builder.d.ts.map +1 -1
  32. package/dist/intelligence/execution-doc-builder.js +459 -42
  33. package/dist/intelligence/execution-doc-builder.js.map +1 -1
  34. package/dist/intelligence/intent-types.d.ts +13 -0
  35. package/dist/intelligence/intent-types.d.ts.map +1 -0
  36. package/dist/intelligence/intent-types.js +19 -0
  37. package/dist/intelligence/intent-types.js.map +1 -0
  38. package/dist/intelligence/multimodal-parser.d.ts +105 -0
  39. package/dist/intelligence/multimodal-parser.d.ts.map +1 -0
  40. package/dist/intelligence/multimodal-parser.js +425 -0
  41. package/dist/intelligence/multimodal-parser.js.map +1 -0
  42. package/dist/web/static/assets/{AIConfig-DiUFET_Q.js → AIConfig-D4VglzCl.js} +2 -2
  43. package/dist/web/static/assets/{AIConfig-DiUFET_Q.js.map → AIConfig-D4VglzCl.js.map} +1 -1
  44. package/dist/web/static/assets/{Agents-bNNGbQnL.js → Agents-ne5lXc7V.js} +2 -2
  45. package/dist/web/static/assets/{Agents-bNNGbQnL.js.map → Agents-ne5lXc7V.js.map} +1 -1
  46. package/dist/web/static/assets/Dashboard-D4j0Zmek.js +2 -0
  47. package/dist/web/static/assets/Dashboard-D4j0Zmek.js.map +1 -0
  48. package/dist/web/static/assets/{Drawer-DOUcx6m1.js → Drawer-Lo5ihVP-.js} +2 -2
  49. package/dist/web/static/assets/{Drawer-DOUcx6m1.js.map → Drawer-Lo5ihVP-.js.map} +1 -1
  50. package/dist/web/static/assets/{Events-DQHP6Uaq.js → Events-DBJ1B7OW.js} +2 -2
  51. package/dist/web/static/assets/{Events-DQHP6Uaq.js.map → Events-DBJ1B7OW.js.map} +1 -1
  52. package/dist/web/static/assets/{ExecutionTrace-Co8ARdg-.js → ExecutionTrace-Du9XADc1.js} +2 -2
  53. package/dist/web/static/assets/{ExecutionTrace-Co8ARdg-.js.map → ExecutionTrace-Du9XADc1.js.map} +1 -1
  54. package/dist/web/static/assets/{Routing-BW3eGD-8.js → Routing-BNQ09OlH.js} +2 -2
  55. package/dist/web/static/assets/{Routing-BW3eGD-8.js.map → Routing-BNQ09OlH.js.map} +1 -1
  56. package/dist/web/static/assets/{SessionDetail-Cbd7Jwox.js → SessionDetail-BPrPyMNa.js} +2 -2
  57. package/dist/web/static/assets/{SessionDetail-Cbd7Jwox.js.map → SessionDetail-BPrPyMNa.js.map} +1 -1
  58. package/dist/web/static/assets/{Sessions-ZQSCgXyy.js → Sessions-o3EXsXz9.js} +2 -2
  59. package/dist/web/static/assets/{Sessions-ZQSCgXyy.js.map → Sessions-o3EXsXz9.js.map} +1 -1
  60. package/dist/web/static/assets/{Skills-C5-5zOSH.js → Skills-Czt5mkyc.js} +2 -2
  61. package/dist/web/static/assets/{Skills-C5-5zOSH.js.map → Skills-Czt5mkyc.js.map} +1 -1
  62. package/dist/web/static/assets/{export-CbQTOt71.js → export-C0mlC4AT.js} +2 -2
  63. package/dist/web/static/assets/{export-CbQTOt71.js.map → export-C0mlC4AT.js.map} +1 -1
  64. package/dist/web/static/assets/index-B1J7nBu0.js +3 -0
  65. package/dist/web/static/assets/index-B1J7nBu0.js.map +1 -0
  66. package/dist/web/static/assets/index-BVqk4bSO.css +1 -0
  67. package/dist/web/static/assets/{lucide-BanPULT1.js → lucide-Bu44HVAM.js} +33 -73
  68. package/dist/web/static/assets/lucide-Bu44HVAM.js.map +1 -0
  69. package/dist/web/static/index.html +3 -3
  70. package/package.json +1 -1
  71. package/dist/web/static/assets/Dashboard-Ciyyw6ph.js +0 -2
  72. package/dist/web/static/assets/Dashboard-Ciyyw6ph.js.map +0 -1
  73. package/dist/web/static/assets/Methodologies-CXNrDXwG.js +0 -5
  74. package/dist/web/static/assets/Methodologies-CXNrDXwG.js.map +0 -1
  75. package/dist/web/static/assets/MethodologyDetail-rV3W1utf.js +0 -2
  76. package/dist/web/static/assets/MethodologyDetail-rV3W1utf.js.map +0 -1
  77. package/dist/web/static/assets/index-DJK5beK6.js +0 -3
  78. package/dist/web/static/assets/index-DJK5beK6.js.map +0 -1
  79. package/dist/web/static/assets/index-phpuytMI.css +0 -1
  80. package/dist/web/static/assets/lucide-BanPULT1.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Sessions-ZQSCgXyy.js","sources":["../../src/components/SessionDetailContent.tsx","../../src/pages/Sessions.tsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { format } from 'date-fns'\nimport { MessageSquare, FileEdit, Terminal, GitCommit } from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface TaskDetail {\n id: string\n title: string\n start_time: string\n end_time?: string\n status: string\n event_count: number\n prompts: Array<{ timestamp: string; content: string }>\n events: Array<any>\n injections: Array<any>\n summary: {\n toolUsage: Record<string, number>\n filesChanged: string[]\n commits: Array<{ timestamp: string; message: string }>\n }\n}\n\ninterface SessionDetailData {\n session: {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n }\n tasks: TaskDetail[]\n}\n\nasync function fetchSessionDetail(id: string): Promise<SessionDetailData> {\n const res = await fetch(`/api/sessions/${id}/detail`)\n if (!res.ok) throw new Error('Failed to fetch session detail')\n return res.json()\n}\n\ninterface Props {\n sessionId: string\n}\n\nexport default function SessionDetailContent({ sessionId }: Props) {\n const { data, isLoading, error } = useQuery({\n queryKey: ['session-detail', sessionId],\n queryFn: () => fetchSessionDetail(sessionId),\n enabled: !!sessionId,\n })\n\n if (isLoading) {\n return <div className=\"p-6 text-gray-500\">加载中...</div>\n }\n\n if (error || !data) {\n return <div className=\"p-6 text-red-600\">加载失败</div>\n }\n\n return (\n <div className=\"space-y-4\">\n <div>\n <div className=\"text-xs text-gray-500 mb-1\">Session ID</div>\n <div className=\"text-sm font-mono text-gray-900 break-all\">{data.session.session_id}</div>\n </div>\n\n <div className=\"grid grid-cols-3 gap-4 pb-4 border-b border-gray-200\">\n <div>\n <div className=\"text-xs text-gray-500\">事件数</div>\n <div className=\"text-lg font-semibold\">{data.session.event_count}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">任务数</div>\n <div className=\"text-lg font-semibold\">{data.tasks.length}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm\">\n {data.session.start_time ? format(new Date(data.session.start_time), 'MM-dd HH:mm:ss') : '-'}\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-3\">任务列表</h3>\n <div className=\"space-y-3\">\n {data.tasks.map((task) => (\n <div key={task.id} className=\"border border-gray-200 rounded-lg p-4\">\n <div className=\"flex items-center justify-between mb-3\">\n <h4 className=\"text-sm font-semibold text-gray-900 flex-1 mr-2\">{task.title}</h4>\n <span className={clsx(\n 'px-2 py-0.5 text-xs font-medium rounded-full whitespace-nowrap',\n task.status === 'completed' && 'bg-green-100 text-green-700',\n task.status === 'active' && 'bg-blue-100 text-blue-700',\n task.status === 'abandoned' && 'bg-gray-100 text-gray-600'\n )}>\n {task.status}\n </span>\n </div>\n\n <div className=\"grid grid-cols-3 gap-3 mb-3 text-xs\">\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <Terminal className=\"h-3 w-3\" />工具使用\n </div>\n <div className=\"space-y-0.5\">\n {Object.entries(task.summary.toolUsage).slice(0, 4).map(([tool, count]) => (\n <div key={tool} className=\"flex justify-between\">\n <span className=\"text-gray-600\">{tool}</span>\n <span className=\"font-mono text-gray-900\">{count}</span>\n </div>\n ))}\n </div>\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <FileEdit className=\"h-3 w-3\" />文件变更\n </div>\n <div className=\"text-gray-700\">{task.summary.filesChanged.length} 个</div>\n {task.summary.filesChanged.slice(0, 3).map(f => (\n <div key={f} className=\"text-gray-500 truncate text-[10px]\" title={f}>\n {f.split('/').pop()}\n </div>\n ))}\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <GitCommit className=\"h-3 w-3\" />提交\n </div>\n <div className=\"text-gray-700\">{task.summary.commits.length} 次</div>\n </div>\n </div>\n\n {task.prompts.length > 0 && (\n <div className=\"border-t border-gray-100 pt-3\">\n <div className=\"text-xs text-gray-500 mb-2 flex items-center gap-1\">\n <MessageSquare className=\"h-3 w-3\" />用户提示 ({task.prompts.length})\n </div>\n <div className=\"space-y-1.5\">\n {task.prompts.slice(0, 3).map((p, i) => (\n <div key={i} className=\"text-xs bg-gray-50 rounded p-2\">\n <p className=\"text-gray-700 line-clamp-2\">{p.content}</p>\n </div>\n ))}\n {task.prompts.length > 3 && (\n <div className=\"text-xs text-gray-400\">还有 {task.prompts.length - 3} 条...</div>\n )}\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { useState, useMemo } from 'react'\nimport { formatDistanceToNow } from 'date-fns'\nimport { zhCN } from 'date-fns/locale'\nimport { MessageSquare, Clock, Activity, Download } from 'lucide-react'\nimport Drawer from '../components/Drawer'\nimport SessionDetailContent from '../components/SessionDetailContent'\nimport SearchInput from '../components/SearchInput'\nimport { downloadJSON, downloadCSV, getTimestamp } from '../utils/export'\nimport { useToast } from '../components/Toast'\n\ninterface Session {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n}\n\nasync function fetchSessions(): Promise<Session[]> {\n const res = await fetch('/api/sessions?limit=200')\n if (!res.ok) throw new Error('Failed to fetch sessions')\n return res.json()\n}\n\nexport default function Sessions() {\n const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null)\n const [search, setSearch] = useState('')\n const [showExportMenu, setShowExportMenu] = useState(false)\n const toast = useToast()\n\n const { data: sessions, isLoading, error } = useQuery({\n queryKey: ['sessions'],\n queryFn: fetchSessions,\n refetchInterval: 10000,\n })\n\n const filteredSessions = useMemo(() => {\n if (!sessions) return []\n if (!search.trim()) return sessions\n const q = search.toLowerCase()\n return sessions.filter(s =>\n s.first_prompt?.toLowerCase().includes(q) ||\n s.session_id.toLowerCase().includes(q)\n )\n }, [sessions, search])\n\n const handleExport = (format: 'json' | 'csv') => {\n const ts = getTimestamp()\n if (format === 'json') {\n downloadJSON(`sessions-${ts}.json`, filteredSessions)\n } else {\n downloadCSV(`sessions-${ts}.csv`, filteredSessions)\n }\n toast.success(`已导出 ${filteredSessions.length} 个会话`)\n setShowExportMenu(false)\n }\n\n if (isLoading) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-gray-600\">加载中...</p>\n </div>\n </div>\n )\n }\n\n if (error) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败: {(error as Error).message}</p>\n </div>\n </div>\n )\n }\n\n return (\n <div>\n <div className=\"flex items-center justify-between mb-4 flex-wrap gap-3\">\n <h1 className=\"text-2xl font-bold text-gray-900\">会话</h1>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <SearchInput\n value={search}\n onChange={setSearch}\n placeholder=\"搜索 session / 提示...\"\n className=\"w-64\"\n />\n <div className=\"relative\">\n <button\n onClick={() => setShowExportMenu(v => !v)}\n disabled={filteredSessions.length === 0}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n 导出\n </button>\n {showExportMenu && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setShowExportMenu(false)} />\n <div className=\"absolute right-0 mt-1 bg-white border border-gray-200 rounded-md shadow-lg z-20\">\n <button\n onClick={() => handleExport('json')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 JSON\n </button>\n <button\n onClick={() => handleExport('csv')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 CSV\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"text-sm text-gray-500 mb-3\">\n 显示 {filteredSessions.length} 个 {search && `(已过滤)`}\n </div>\n\n <div className=\"bg-white rounded-lg shadow overflow-hidden\">\n <table className=\"min-w-full divide-y divide-gray-200\">\n <thead className=\"bg-gray-50\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n Session ID\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 首次提示\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 事件数\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 时间\n </th>\n </tr>\n </thead>\n <tbody className=\"bg-white divide-y divide-gray-200\">\n {filteredSessions.map((session) => (\n <tr\n key={session.session_id}\n className=\"hover:bg-gray-50 cursor-pointer\"\n onClick={() => setSelectedSessionId(session.session_id)}\n >\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"flex items-center gap-2 text-sm text-indigo-600 font-mono\">\n <MessageSquare className=\"h-4 w-4\" />\n {session.session_id.slice(0, 12)}...\n </div>\n </td>\n <td className=\"px-6 py-4\">\n <p className=\"text-sm text-gray-900 max-w-md truncate\">\n {session.first_prompt || '(无提示)'}\n </p>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <span className=\"inline-flex items-center gap-1 text-sm text-gray-600\">\n <Activity className=\"h-4 w-4\" />\n {session.event_count}\n </span>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-4 w-4\" />\n {session.start_time\n ? formatDistanceToNow(new Date(session.start_time), {\n addSuffix: true,\n locale: zhCN,\n })\n : '-'}\n </span>\n </td>\n </tr>\n ))}\n {filteredSessions.length === 0 && (\n <tr>\n <td colSpan={4} className=\"px-6 py-12 text-center text-gray-500\">\n {search ? '无匹配结果' : '暂无会话记录'}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n\n <Drawer\n open={selectedSessionId !== null}\n onClose={() => setSelectedSessionId(null)}\n title=\"会话详情\"\n width=\"w-[800px]\"\n >\n {selectedSessionId && <SessionDetailContent sessionId={selectedSessionId} />}\n </Drawer>\n </div>\n )\n}\n"],"names":["fetchSessionDetail","id","res","SessionDetailContent","sessionId","data","isLoading","error","useQuery","jsx","jsxs","format","task","clsx","Terminal","tool","count","FileEdit","f","GitCommit","MessageSquare","p","i","fetchSessions","Sessions","selectedSessionId","setSelectedSessionId","useState","search","setSearch","showExportMenu","setShowExportMenu","toast","useToast","sessions","filteredSessions","useMemo","q","s","_a","handleExport","ts","getTimestamp","downloadJSON","downloadCSV","SearchInput","v","Download","Fragment","session","Activity","Clock","formatDistanceToNow","zhCN","Drawer"],"mappings":"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"}
1
+ {"version":3,"file":"Sessions-o3EXsXz9.js","sources":["../../src/components/SessionDetailContent.tsx","../../src/pages/Sessions.tsx"],"sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { format } from 'date-fns'\nimport { MessageSquare, FileEdit, Terminal, GitCommit } from 'lucide-react'\nimport clsx from 'clsx'\n\ninterface TaskDetail {\n id: string\n title: string\n start_time: string\n end_time?: string\n status: string\n event_count: number\n prompts: Array<{ timestamp: string; content: string }>\n events: Array<any>\n injections: Array<any>\n summary: {\n toolUsage: Record<string, number>\n filesChanged: string[]\n commits: Array<{ timestamp: string; message: string }>\n }\n}\n\ninterface SessionDetailData {\n session: {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n }\n tasks: TaskDetail[]\n}\n\nasync function fetchSessionDetail(id: string): Promise<SessionDetailData> {\n const res = await fetch(`/api/sessions/${id}/detail`)\n if (!res.ok) throw new Error('Failed to fetch session detail')\n return res.json()\n}\n\ninterface Props {\n sessionId: string\n}\n\nexport default function SessionDetailContent({ sessionId }: Props) {\n const { data, isLoading, error } = useQuery({\n queryKey: ['session-detail', sessionId],\n queryFn: () => fetchSessionDetail(sessionId),\n enabled: !!sessionId,\n })\n\n if (isLoading) {\n return <div className=\"p-6 text-gray-500\">加载中...</div>\n }\n\n if (error || !data) {\n return <div className=\"p-6 text-red-600\">加载失败</div>\n }\n\n return (\n <div className=\"space-y-4\">\n <div>\n <div className=\"text-xs text-gray-500 mb-1\">Session ID</div>\n <div className=\"text-sm font-mono text-gray-900 break-all\">{data.session.session_id}</div>\n </div>\n\n <div className=\"grid grid-cols-3 gap-4 pb-4 border-b border-gray-200\">\n <div>\n <div className=\"text-xs text-gray-500\">事件数</div>\n <div className=\"text-lg font-semibold\">{data.session.event_count}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">任务数</div>\n <div className=\"text-lg font-semibold\">{data.tasks.length}</div>\n </div>\n <div>\n <div className=\"text-xs text-gray-500\">开始时间</div>\n <div className=\"text-sm\">\n {data.session.start_time ? format(new Date(data.session.start_time), 'MM-dd HH:mm:ss') : '-'}\n </div>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-sm font-semibold text-gray-700 mb-3\">任务列表</h3>\n <div className=\"space-y-3\">\n {data.tasks.map((task) => (\n <div key={task.id} className=\"border border-gray-200 rounded-lg p-4\">\n <div className=\"flex items-center justify-between mb-3\">\n <h4 className=\"text-sm font-semibold text-gray-900 flex-1 mr-2\">{task.title}</h4>\n <span className={clsx(\n 'px-2 py-0.5 text-xs font-medium rounded-full whitespace-nowrap',\n task.status === 'completed' && 'bg-green-100 text-green-700',\n task.status === 'active' && 'bg-blue-100 text-blue-700',\n task.status === 'abandoned' && 'bg-gray-100 text-gray-600'\n )}>\n {task.status}\n </span>\n </div>\n\n <div className=\"grid grid-cols-3 gap-3 mb-3 text-xs\">\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <Terminal className=\"h-3 w-3\" />工具使用\n </div>\n <div className=\"space-y-0.5\">\n {Object.entries(task.summary.toolUsage).slice(0, 4).map(([tool, count]) => (\n <div key={tool} className=\"flex justify-between\">\n <span className=\"text-gray-600\">{tool}</span>\n <span className=\"font-mono text-gray-900\">{count}</span>\n </div>\n ))}\n </div>\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <FileEdit className=\"h-3 w-3\" />文件变更\n </div>\n <div className=\"text-gray-700\">{task.summary.filesChanged.length} 个</div>\n {task.summary.filesChanged.slice(0, 3).map(f => (\n <div key={f} className=\"text-gray-500 truncate text-[10px]\" title={f}>\n {f.split('/').pop()}\n </div>\n ))}\n </div>\n <div>\n <div className=\"text-gray-500 mb-1 flex items-center gap-1\">\n <GitCommit className=\"h-3 w-3\" />提交\n </div>\n <div className=\"text-gray-700\">{task.summary.commits.length} 次</div>\n </div>\n </div>\n\n {task.prompts.length > 0 && (\n <div className=\"border-t border-gray-100 pt-3\">\n <div className=\"text-xs text-gray-500 mb-2 flex items-center gap-1\">\n <MessageSquare className=\"h-3 w-3\" />用户提示 ({task.prompts.length})\n </div>\n <div className=\"space-y-1.5\">\n {task.prompts.slice(0, 3).map((p, i) => (\n <div key={i} className=\"text-xs bg-gray-50 rounded p-2\">\n <p className=\"text-gray-700 line-clamp-2\">{p.content}</p>\n </div>\n ))}\n {task.prompts.length > 3 && (\n <div className=\"text-xs text-gray-400\">还有 {task.prompts.length - 3} 条...</div>\n )}\n </div>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )\n}\n","import { useQuery } from '@tanstack/react-query'\nimport { useState, useMemo } from 'react'\nimport { formatDistanceToNow } from 'date-fns'\nimport { zhCN } from 'date-fns/locale'\nimport { MessageSquare, Clock, Activity, Download } from 'lucide-react'\nimport Drawer from '../components/Drawer'\nimport SessionDetailContent from '../components/SessionDetailContent'\nimport SearchInput from '../components/SearchInput'\nimport { downloadJSON, downloadCSV, getTimestamp } from '../utils/export'\nimport { useToast } from '../components/Toast'\n\ninterface Session {\n session_id: string\n first_prompt: string\n event_count: number\n start_time: string\n end_time: string\n}\n\nasync function fetchSessions(): Promise<Session[]> {\n const res = await fetch('/api/sessions?limit=200')\n if (!res.ok) throw new Error('Failed to fetch sessions')\n return res.json()\n}\n\nexport default function Sessions() {\n const [selectedSessionId, setSelectedSessionId] = useState<string | null>(null)\n const [search, setSearch] = useState('')\n const [showExportMenu, setShowExportMenu] = useState(false)\n const toast = useToast()\n\n const { data: sessions, isLoading, error } = useQuery({\n queryKey: ['sessions'],\n queryFn: fetchSessions,\n refetchInterval: 10000,\n })\n\n const filteredSessions = useMemo(() => {\n if (!sessions) return []\n if (!search.trim()) return sessions\n const q = search.toLowerCase()\n return sessions.filter(s =>\n s.first_prompt?.toLowerCase().includes(q) ||\n s.session_id.toLowerCase().includes(q)\n )\n }, [sessions, search])\n\n const handleExport = (format: 'json' | 'csv') => {\n const ts = getTimestamp()\n if (format === 'json') {\n downloadJSON(`sessions-${ts}.json`, filteredSessions)\n } else {\n downloadCSV(`sessions-${ts}.csv`, filteredSessions)\n }\n toast.success(`已导出 ${filteredSessions.length} 个会话`)\n setShowExportMenu(false)\n }\n\n if (isLoading) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-gray-600\">加载中...</p>\n </div>\n </div>\n )\n }\n\n if (error) {\n return (\n <div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-6\">会话</h1>\n <div className=\"bg-white rounded-lg shadow p-6\">\n <p className=\"text-red-600\">加载失败: {(error as Error).message}</p>\n </div>\n </div>\n )\n }\n\n return (\n <div>\n <div className=\"flex items-center justify-between mb-4 flex-wrap gap-3\">\n <h1 className=\"text-2xl font-bold text-gray-900\">会话</h1>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <SearchInput\n value={search}\n onChange={setSearch}\n placeholder=\"搜索 session / 提示...\"\n className=\"w-64\"\n />\n <div className=\"relative\">\n <button\n onClick={() => setShowExportMenu(v => !v)}\n disabled={filteredSessions.length === 0}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50\"\n >\n <Download className=\"h-4 w-4\" />\n 导出\n </button>\n {showExportMenu && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setShowExportMenu(false)} />\n <div className=\"absolute right-0 mt-1 bg-white border border-gray-200 rounded-md shadow-lg z-20\">\n <button\n onClick={() => handleExport('json')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 JSON\n </button>\n <button\n onClick={() => handleExport('csv')}\n className=\"block w-full text-left px-4 py-2 text-sm hover:bg-gray-50\"\n >\n 导出为 CSV\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n </div>\n\n <div className=\"text-sm text-gray-500 mb-3\">\n 显示 {filteredSessions.length} 个 {search && `(已过滤)`}\n </div>\n\n <div className=\"bg-white rounded-lg shadow overflow-hidden\">\n <table className=\"min-w-full divide-y divide-gray-200\">\n <thead className=\"bg-gray-50\">\n <tr>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n Session ID\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 首次提示\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 事件数\n </th>\n <th className=\"px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider\">\n 时间\n </th>\n </tr>\n </thead>\n <tbody className=\"bg-white divide-y divide-gray-200\">\n {filteredSessions.map((session) => (\n <tr\n key={session.session_id}\n className=\"hover:bg-gray-50 cursor-pointer\"\n onClick={() => setSelectedSessionId(session.session_id)}\n >\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <div className=\"flex items-center gap-2 text-sm text-indigo-600 font-mono\">\n <MessageSquare className=\"h-4 w-4\" />\n {session.session_id.slice(0, 12)}...\n </div>\n </td>\n <td className=\"px-6 py-4\">\n <p className=\"text-sm text-gray-900 max-w-md truncate\">\n {session.first_prompt || '(无提示)'}\n </p>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap\">\n <span className=\"inline-flex items-center gap-1 text-sm text-gray-600\">\n <Activity className=\"h-4 w-4\" />\n {session.event_count}\n </span>\n </td>\n <td className=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-4 w-4\" />\n {session.start_time\n ? formatDistanceToNow(new Date(session.start_time), {\n addSuffix: true,\n locale: zhCN,\n })\n : '-'}\n </span>\n </td>\n </tr>\n ))}\n {filteredSessions.length === 0 && (\n <tr>\n <td colSpan={4} className=\"px-6 py-12 text-center text-gray-500\">\n {search ? '无匹配结果' : '暂无会话记录'}\n </td>\n </tr>\n )}\n </tbody>\n </table>\n </div>\n\n <Drawer\n open={selectedSessionId !== null}\n onClose={() => setSelectedSessionId(null)}\n title=\"会话详情\"\n width=\"w-[800px]\"\n >\n {selectedSessionId && <SessionDetailContent sessionId={selectedSessionId} />}\n </Drawer>\n </div>\n )\n}\n"],"names":["fetchSessionDetail","id","res","SessionDetailContent","sessionId","data","isLoading","error","useQuery","jsx","jsxs","format","task","clsx","Terminal","tool","count","FileEdit","f","GitCommit","MessageSquare","p","i","fetchSessions","Sessions","selectedSessionId","setSelectedSessionId","useState","search","setSearch","showExportMenu","setShowExportMenu","toast","useToast","sessions","filteredSessions","useMemo","q","s","_a","handleExport","ts","getTimestamp","downloadJSON","downloadCSV","SearchInput","v","Download","Fragment","session","Activity","Clock","formatDistanceToNow","zhCN","Drawer"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as c,j as e}from"./react-vendor-CSp-GLFF.js";import{b as p,u,c as j}from"./query-C99w429o.js";import{D as y}from"./Drawer-DOUcx6m1.js";import{M as w}from"./MarkdownRenderer-CCIz1MOz.js";import{u as b}from"./index-DJK5beK6.js";import{a as v}from"./auth-Bnf8ZcqN.js";import{b as g,t as N,X as k,u as S}from"./lucide-BanPULT1.js";import"./vendor-CMMjVdZs.js";import"./syntax-highlighter-44FakypI.js";import"./react-router-I-HqunH7.js";async function C(){const i=await fetch("/api/skills");if(!i.ok)throw new Error("Failed to fetch skills");const s=await i.json();return Array.isArray(s)?s:s.skills||[]}async function E(i){const s=await fetch(`/api/skills/${i}`);if(!s.ok)throw new Error("Failed to fetch");return s.json()}async function F(i,s){if(!(await v(`/api/skills/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:s})})).ok)throw new Error("Failed to save")}function R(){const i=p(),s=b(),{data:n,isLoading:f}=u({queryKey:["skills"],queryFn:C}),[r,x]=c.useState(null),[m,l]=c.useState(!1),[h,d]=c.useState(""),{data:t}=u({queryKey:["skill-detail",r],queryFn:()=>E(r),enabled:!!r});c.useEffect(()=>{t!=null&&t.content&&d(t.content),l(!1)},[t]);const o=j({mutationFn:a=>F(a.name,a.content),onSuccess:()=>{s.success("Skill 已保存"),l(!1),i.invalidateQueries({queryKey:["skill-detail",r]}),i.invalidateQueries({queryKey:["skills"]})},onError:a=>s.error(`保存失败: ${a.message}`)});return f?e.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:"加载中..."}):e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-6",children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:"Skill 管理"}),e.jsxs("div",{className:"text-sm text-gray-500",children:["共 ",(n==null?void 0:n.length)||0," 个 Skill"]})]}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",children:n==null?void 0:n.map(a=>e.jsx("div",{className:"bg-white rounded-lg shadow p-5 hover:shadow-md transition-shadow cursor-pointer",onClick:()=>x(a.name),children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0",children:e.jsx(g,{className:"h-5 w-5 text-purple-600"})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("h3",{className:"font-semibold text-gray-900 truncate",children:a.name}),e.jsx("p",{className:"text-xs text-gray-500 mt-1 line-clamp-3",children:a.description||"(无描述)"})]})]})},a.name))}),e.jsx(y,{open:r!==null,onClose:()=>{x(null),l(!1)},title:`Skill: ${r}`,width:"w-[800px]",children:t&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(g,{className:"h-5 w-5 text-purple-600"}),e.jsx("h3",{className:"text-lg font-semibold text-gray-900",children:t.name})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500 mb-1",children:"描述"}),e.jsx("div",{className:"text-sm text-gray-800",children:t.description})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("div",{className:"text-xs text-gray-500",children:"完整内容 (Markdown)"}),e.jsx("div",{className:"flex items-center gap-2",children:m?e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>{l(!1),d(t.content||"")},className:"inline-flex items-center gap-1 text-xs text-gray-600 hover:text-gray-700",children:[e.jsx(k,{className:"h-3 w-3"}),"取消"]}),e.jsxs("button",{onClick:()=>o.mutate({name:t.name,content:h}),disabled:o.isPending,className:"inline-flex items-center gap-1 text-xs bg-indigo-600 text-white px-2 py-1 rounded hover:bg-indigo-700 disabled:bg-gray-400",children:[e.jsx(S,{className:"h-3 w-3"}),o.isPending?"保存中...":"保存"]})]}):e.jsxs("button",{onClick:()=>l(!0),className:"inline-flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700",children:[e.jsx(N,{className:"h-3 w-3"}),"编辑"]})})]}),m?e.jsx("textarea",{value:h,onChange:a=>d(a.target.value),className:"w-full font-mono text-xs bg-gray-50 rounded p-3 border border-gray-300 focus:border-indigo-500 focus:outline-none",rows:20}):e.jsx("div",{className:"bg-white rounded p-3 border border-gray-200 max-h-[500px] overflow-y-auto",children:e.jsx(w,{content:t.content||""})}),o.isError&&e.jsxs("div",{className:"text-xs text-red-600 mt-1",children:["保存失败:",o.error.message]})]})]})})]})}export{R as default};
2
- //# sourceMappingURL=Skills-C5-5zOSH.js.map
1
+ import{r as c,j as e}from"./react-vendor-CSp-GLFF.js";import{b as p,u,c as j}from"./query-C99w429o.js";import{D as y}from"./Drawer-Lo5ihVP-.js";import{M as w}from"./MarkdownRenderer-CCIz1MOz.js";import{u as b}from"./index-B1J7nBu0.js";import{a as v}from"./auth-Bnf8ZcqN.js";import{b as g,P as N,X as k,m as S}from"./lucide-Bu44HVAM.js";import"./vendor-CMMjVdZs.js";import"./syntax-highlighter-44FakypI.js";import"./react-router-I-HqunH7.js";async function C(){const i=await fetch("/api/skills");if(!i.ok)throw new Error("Failed to fetch skills");const s=await i.json();return Array.isArray(s)?s:s.skills||[]}async function E(i){const s=await fetch(`/api/skills/${i}`);if(!s.ok)throw new Error("Failed to fetch");return s.json()}async function F(i,s){if(!(await v(`/api/skills/${i}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:s})})).ok)throw new Error("Failed to save")}function R(){const i=p(),s=b(),{data:n,isLoading:f}=u({queryKey:["skills"],queryFn:C}),[r,x]=c.useState(null),[m,l]=c.useState(!1),[h,d]=c.useState(""),{data:t}=u({queryKey:["skill-detail",r],queryFn:()=>E(r),enabled:!!r});c.useEffect(()=>{t!=null&&t.content&&d(t.content),l(!1)},[t]);const o=j({mutationFn:a=>F(a.name,a.content),onSuccess:()=>{s.success("Skill 已保存"),l(!1),i.invalidateQueries({queryKey:["skill-detail",r]}),i.invalidateQueries({queryKey:["skills"]})},onError:a=>s.error(`保存失败: ${a.message}`)});return f?e.jsx("div",{className:"bg-white rounded-lg shadow p-6",children:"加载中..."}):e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-6",children:[e.jsx("h1",{className:"text-2xl font-bold text-gray-900",children:"Skill 管理"}),e.jsxs("div",{className:"text-sm text-gray-500",children:["共 ",(n==null?void 0:n.length)||0," 个 Skill"]})]}),e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",children:n==null?void 0:n.map(a=>e.jsx("div",{className:"bg-white rounded-lg shadow p-5 hover:shadow-md transition-shadow cursor-pointer",onClick:()=>x(a.name),children:e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0",children:e.jsx(g,{className:"h-5 w-5 text-purple-600"})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("h3",{className:"font-semibold text-gray-900 truncate",children:a.name}),e.jsx("p",{className:"text-xs text-gray-500 mt-1 line-clamp-3",children:a.description||"(无描述)"})]})]})},a.name))}),e.jsx(y,{open:r!==null,onClose:()=>{x(null),l(!1)},title:`Skill: ${r}`,width:"w-[800px]",children:t&&e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-2",children:[e.jsx(g,{className:"h-5 w-5 text-purple-600"}),e.jsx("h3",{className:"text-lg font-semibold text-gray-900",children:t.name})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-gray-500 mb-1",children:"描述"}),e.jsx("div",{className:"text-sm text-gray-800",children:t.description})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("div",{className:"text-xs text-gray-500",children:"完整内容 (Markdown)"}),e.jsx("div",{className:"flex items-center gap-2",children:m?e.jsxs(e.Fragment,{children:[e.jsxs("button",{onClick:()=>{l(!1),d(t.content||"")},className:"inline-flex items-center gap-1 text-xs text-gray-600 hover:text-gray-700",children:[e.jsx(k,{className:"h-3 w-3"}),"取消"]}),e.jsxs("button",{onClick:()=>o.mutate({name:t.name,content:h}),disabled:o.isPending,className:"inline-flex items-center gap-1 text-xs bg-indigo-600 text-white px-2 py-1 rounded hover:bg-indigo-700 disabled:bg-gray-400",children:[e.jsx(S,{className:"h-3 w-3"}),o.isPending?"保存中...":"保存"]})]}):e.jsxs("button",{onClick:()=>l(!0),className:"inline-flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700",children:[e.jsx(N,{className:"h-3 w-3"}),"编辑"]})})]}),m?e.jsx("textarea",{value:h,onChange:a=>d(a.target.value),className:"w-full font-mono text-xs bg-gray-50 rounded p-3 border border-gray-300 focus:border-indigo-500 focus:outline-none",rows:20}):e.jsx("div",{className:"bg-white rounded p-3 border border-gray-200 max-h-[500px] overflow-y-auto",children:e.jsx(w,{content:t.content||""})}),o.isError&&e.jsxs("div",{className:"text-xs text-red-600 mt-1",children:["保存失败:",o.error.message]})]})]})})]})}export{R as default};
2
+ //# sourceMappingURL=Skills-Czt5mkyc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Skills-C5-5zOSH.js","sources":["../../src/pages/Skills.tsx"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { useState, useEffect } from 'react'\nimport { BookOpen, Edit2, Save, X } from 'lucide-react'\nimport Drawer from '../components/Drawer'\nimport MarkdownRenderer from '../components/MarkdownRenderer'\nimport { useToast } from '../components/Toast'\nimport { authFetch } from '../utils/auth'\n\ninterface Skill {\n name: string\n description: string\n content?: string\n}\n\ninterface SkillsResponse {\n skills?: Skill[]\n}\n\nasync function fetchSkills(): Promise<Skill[]> {\n const res = await fetch('/api/skills')\n if (!res.ok) throw new Error('Failed to fetch skills')\n const data: SkillsResponse | Skill[] = await res.json()\n if (Array.isArray(data)) return data\n return data.skills || []\n}\n\nasync function fetchSkillDetail(name: string): Promise<Skill> {\n const res = await fetch(`/api/skills/${name}`)\n if (!res.ok) throw new Error('Failed to fetch')\n return res.json()\n}\n\nasync function saveSkill(name: string, content: string): Promise<void> {\n const res = await authFetch(`/api/skills/${name}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ content }),\n })\n if (!res.ok) throw new Error('Failed to save')\n}\n\nexport default function Skills() {\n const queryClient = useQueryClient()\n const toast = useToast()\n const { data: skills, isLoading } = useQuery({\n queryKey: ['skills'],\n queryFn: fetchSkills,\n })\n\n const [selectedSkill, setSelectedSkill] = useState<string | null>(null)\n const [editing, setEditing] = useState(false)\n const [editContent, setEditContent] = useState('')\n\n const { data: detail } = useQuery({\n queryKey: ['skill-detail', selectedSkill],\n queryFn: () => fetchSkillDetail(selectedSkill!),\n enabled: !!selectedSkill,\n })\n\n useEffect(() => {\n if (detail?.content) setEditContent(detail.content)\n setEditing(false)\n }, [detail])\n\n const mutation = useMutation({\n mutationFn: (vars: { name: string; content: string }) => saveSkill(vars.name, vars.content),\n onSuccess: () => {\n toast.success('Skill 已保存')\n setEditing(false)\n queryClient.invalidateQueries({ queryKey: ['skill-detail', selectedSkill] })\n queryClient.invalidateQueries({ queryKey: ['skills'] })\n },\n onError: (e: Error) => toast.error(`保存失败: ${e.message}`),\n })\n\n if (isLoading) {\n return <div className=\"bg-white rounded-lg shadow p-6\">加载中...</div>\n }\n\n return (\n <div>\n <div className=\"flex items-center justify-between mb-6\">\n <h1 className=\"text-2xl font-bold text-gray-900\">Skill 管理</h1>\n <div className=\"text-sm text-gray-500\">共 {skills?.length || 0} 个 Skill</div>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {skills?.map((skill) => (\n <div\n key={skill.name}\n className=\"bg-white rounded-lg shadow p-5 hover:shadow-md transition-shadow cursor-pointer\"\n onClick={() => setSelectedSkill(skill.name)}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0\">\n <BookOpen className=\"h-5 w-5 text-purple-600\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-semibold text-gray-900 truncate\">{skill.name}</h3>\n <p className=\"text-xs text-gray-500 mt-1 line-clamp-3\">\n {skill.description || '(无描述)'}\n </p>\n </div>\n </div>\n </div>\n ))}\n </div>\n\n {/* Skill Detail Drawer */}\n <Drawer\n open={selectedSkill !== null}\n onClose={() => { setSelectedSkill(null); setEditing(false) }}\n title={`Skill: ${selectedSkill}`}\n width=\"w-[800px]\"\n >\n {detail && (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mb-2\">\n <BookOpen className=\"h-5 w-5 text-purple-600\" />\n <h3 className=\"text-lg font-semibold text-gray-900\">{detail.name}</h3>\n </div>\n\n <div>\n <div className=\"text-xs text-gray-500 mb-1\">描述</div>\n <div className=\"text-sm text-gray-800\">{detail.description}</div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"text-xs text-gray-500\">完整内容 (Markdown)</div>\n <div className=\"flex items-center gap-2\">\n {!editing ? (\n <button\n onClick={() => setEditing(true)}\n className=\"inline-flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700\"\n >\n <Edit2 className=\"h-3 w-3\" />编辑\n </button>\n ) : (\n <>\n <button\n onClick={() => { setEditing(false); setEditContent(detail.content || '') }}\n className=\"inline-flex items-center gap-1 text-xs text-gray-600 hover:text-gray-700\"\n >\n <X className=\"h-3 w-3\" />取消\n </button>\n <button\n onClick={() => mutation.mutate({ name: detail.name, content: editContent })}\n disabled={mutation.isPending}\n className=\"inline-flex items-center gap-1 text-xs bg-indigo-600 text-white px-2 py-1 rounded hover:bg-indigo-700 disabled:bg-gray-400\"\n >\n <Save className=\"h-3 w-3\" />\n {mutation.isPending ? '保存中...' : '保存'}\n </button>\n </>\n )}\n </div>\n </div>\n {editing ? (\n <textarea\n value={editContent}\n onChange={(e) => setEditContent(e.target.value)}\n className=\"w-full font-mono text-xs bg-gray-50 rounded p-3 border border-gray-300 focus:border-indigo-500 focus:outline-none\"\n rows={20}\n />\n ) : (\n <div className=\"bg-white rounded p-3 border border-gray-200 max-h-[500px] overflow-y-auto\">\n <MarkdownRenderer content={detail.content || ''} />\n </div>\n )}\n {mutation.isError && (\n <div className=\"text-xs text-red-600 mt-1\">保存失败:{(mutation.error as Error).message}</div>\n )}\n </div>\n </div>\n )}\n </Drawer>\n </div>\n )\n}\n"],"names":["fetchSkills","res","data","fetchSkillDetail","name","saveSkill","content","authFetch","Skills","queryClient","useQueryClient","toast","useToast","skills","isLoading","useQuery","selectedSkill","setSelectedSkill","useState","editing","setEditing","editContent","setEditContent","detail","useEffect","mutation","useMutation","vars","e","jsx","jsxs","skill","BookOpen","Drawer","Fragment","X","Save","Edit2","MarkdownRenderer"],"mappings":"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"}
1
+ {"version":3,"file":"Skills-Czt5mkyc.js","sources":["../../src/pages/Skills.tsx"],"sourcesContent":["import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'\nimport { useState, useEffect } from 'react'\nimport { BookOpen, Edit2, Save, X } from 'lucide-react'\nimport Drawer from '../components/Drawer'\nimport MarkdownRenderer from '../components/MarkdownRenderer'\nimport { useToast } from '../components/Toast'\nimport { authFetch } from '../utils/auth'\n\ninterface Skill {\n name: string\n description: string\n content?: string\n}\n\ninterface SkillsResponse {\n skills?: Skill[]\n}\n\nasync function fetchSkills(): Promise<Skill[]> {\n const res = await fetch('/api/skills')\n if (!res.ok) throw new Error('Failed to fetch skills')\n const data: SkillsResponse | Skill[] = await res.json()\n if (Array.isArray(data)) return data\n return data.skills || []\n}\n\nasync function fetchSkillDetail(name: string): Promise<Skill> {\n const res = await fetch(`/api/skills/${name}`)\n if (!res.ok) throw new Error('Failed to fetch')\n return res.json()\n}\n\nasync function saveSkill(name: string, content: string): Promise<void> {\n const res = await authFetch(`/api/skills/${name}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ content }),\n })\n if (!res.ok) throw new Error('Failed to save')\n}\n\nexport default function Skills() {\n const queryClient = useQueryClient()\n const toast = useToast()\n const { data: skills, isLoading } = useQuery({\n queryKey: ['skills'],\n queryFn: fetchSkills,\n })\n\n const [selectedSkill, setSelectedSkill] = useState<string | null>(null)\n const [editing, setEditing] = useState(false)\n const [editContent, setEditContent] = useState('')\n\n const { data: detail } = useQuery({\n queryKey: ['skill-detail', selectedSkill],\n queryFn: () => fetchSkillDetail(selectedSkill!),\n enabled: !!selectedSkill,\n })\n\n useEffect(() => {\n if (detail?.content) setEditContent(detail.content)\n setEditing(false)\n }, [detail])\n\n const mutation = useMutation({\n mutationFn: (vars: { name: string; content: string }) => saveSkill(vars.name, vars.content),\n onSuccess: () => {\n toast.success('Skill 已保存')\n setEditing(false)\n queryClient.invalidateQueries({ queryKey: ['skill-detail', selectedSkill] })\n queryClient.invalidateQueries({ queryKey: ['skills'] })\n },\n onError: (e: Error) => toast.error(`保存失败: ${e.message}`),\n })\n\n if (isLoading) {\n return <div className=\"bg-white rounded-lg shadow p-6\">加载中...</div>\n }\n\n return (\n <div>\n <div className=\"flex items-center justify-between mb-6\">\n <h1 className=\"text-2xl font-bold text-gray-900\">Skill 管理</h1>\n <div className=\"text-sm text-gray-500\">共 {skills?.length || 0} 个 Skill</div>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {skills?.map((skill) => (\n <div\n key={skill.name}\n className=\"bg-white rounded-lg shadow p-5 hover:shadow-md transition-shadow cursor-pointer\"\n onClick={() => setSelectedSkill(skill.name)}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"w-10 h-10 rounded-lg bg-purple-100 flex items-center justify-center flex-shrink-0\">\n <BookOpen className=\"h-5 w-5 text-purple-600\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-semibold text-gray-900 truncate\">{skill.name}</h3>\n <p className=\"text-xs text-gray-500 mt-1 line-clamp-3\">\n {skill.description || '(无描述)'}\n </p>\n </div>\n </div>\n </div>\n ))}\n </div>\n\n {/* Skill Detail Drawer */}\n <Drawer\n open={selectedSkill !== null}\n onClose={() => { setSelectedSkill(null); setEditing(false) }}\n title={`Skill: ${selectedSkill}`}\n width=\"w-[800px]\"\n >\n {detail && (\n <div className=\"space-y-4\">\n <div className=\"flex items-center gap-2 mb-2\">\n <BookOpen className=\"h-5 w-5 text-purple-600\" />\n <h3 className=\"text-lg font-semibold text-gray-900\">{detail.name}</h3>\n </div>\n\n <div>\n <div className=\"text-xs text-gray-500 mb-1\">描述</div>\n <div className=\"text-sm text-gray-800\">{detail.description}</div>\n </div>\n\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <div className=\"text-xs text-gray-500\">完整内容 (Markdown)</div>\n <div className=\"flex items-center gap-2\">\n {!editing ? (\n <button\n onClick={() => setEditing(true)}\n className=\"inline-flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-700\"\n >\n <Edit2 className=\"h-3 w-3\" />编辑\n </button>\n ) : (\n <>\n <button\n onClick={() => { setEditing(false); setEditContent(detail.content || '') }}\n className=\"inline-flex items-center gap-1 text-xs text-gray-600 hover:text-gray-700\"\n >\n <X className=\"h-3 w-3\" />取消\n </button>\n <button\n onClick={() => mutation.mutate({ name: detail.name, content: editContent })}\n disabled={mutation.isPending}\n className=\"inline-flex items-center gap-1 text-xs bg-indigo-600 text-white px-2 py-1 rounded hover:bg-indigo-700 disabled:bg-gray-400\"\n >\n <Save className=\"h-3 w-3\" />\n {mutation.isPending ? '保存中...' : '保存'}\n </button>\n </>\n )}\n </div>\n </div>\n {editing ? (\n <textarea\n value={editContent}\n onChange={(e) => setEditContent(e.target.value)}\n className=\"w-full font-mono text-xs bg-gray-50 rounded p-3 border border-gray-300 focus:border-indigo-500 focus:outline-none\"\n rows={20}\n />\n ) : (\n <div className=\"bg-white rounded p-3 border border-gray-200 max-h-[500px] overflow-y-auto\">\n <MarkdownRenderer content={detail.content || ''} />\n </div>\n )}\n {mutation.isError && (\n <div className=\"text-xs text-red-600 mt-1\">保存失败:{(mutation.error as Error).message}</div>\n )}\n </div>\n </div>\n )}\n </Drawer>\n </div>\n )\n}\n"],"names":["fetchSkills","res","data","fetchSkillDetail","name","saveSkill","content","authFetch","Skills","queryClient","useQueryClient","toast","useToast","skills","isLoading","useQuery","selectedSkill","setSelectedSkill","useState","editing","setEditing","editContent","setEditContent","detail","useEffect","mutation","useMutation","vars","e","jsx","jsxs","skill","BookOpen","Drawer","Fragment","X","Save","Edit2","MarkdownRenderer"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{j as r}from"./react-vendor-CSp-GLFF.js";import{S as l,X as d}from"./lucide-BanPULT1.js";function m({value:e,onChange:t,placeholder:n,className:o=""}){return r.jsxs("div",{className:`relative ${o}`,children:[r.jsx(l,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400"}),r.jsx("input",{type:"text",value:e,onChange:s=>t(s.target.value),placeholder:n||"搜索...",className:"w-full pl-8 pr-8 py-1.5 text-sm border border-gray-300 rounded-md bg-white focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"}),e&&r.jsx("button",{onClick:()=>t(""),className:"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600",children:r.jsx(d,{className:"h-4 w-4"})})]})}function u(e,t){const n=JSON.stringify(t,null,2),o=new Blob([n],{type:"application/json"});c(e,o)}function b(e,t){if(!t||t.length===0){u(e.replace(/\.csv$/,".json"),t);return}const n=Object.keys(t[0]),o=[n.join(","),...t.map(a=>n.map(i=>p(a[i])).join(","))].join(`
1
+ import{j as r}from"./react-vendor-CSp-GLFF.js";import{S as l,X as d}from"./lucide-Bu44HVAM.js";function m({value:e,onChange:t,placeholder:n,className:o=""}){return r.jsxs("div",{className:`relative ${o}`,children:[r.jsx(l,{className:"absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400"}),r.jsx("input",{type:"text",value:e,onChange:s=>t(s.target.value),placeholder:n||"搜索...",className:"w-full pl-8 pr-8 py-1.5 text-sm border border-gray-300 rounded-md bg-white focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"}),e&&r.jsx("button",{onClick:()=>t(""),className:"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600",children:r.jsx(d,{className:"h-4 w-4"})})]})}function u(e,t){const n=JSON.stringify(t,null,2),o=new Blob([n],{type:"application/json"});c(e,o)}function b(e,t){if(!t||t.length===0){u(e.replace(/\.csv$/,".json"),t);return}const n=Object.keys(t[0]),o=[n.join(","),...t.map(a=>n.map(i=>p(a[i])).join(","))].join(`
2
2
  `),s=new Blob(["\uFEFF"+o],{type:"text/csv;charset=utf-8"});c(e,s)}function p(e){if(e==null)return"";const t=typeof e=="object"?JSON.stringify(e):String(e);return t.includes(",")||t.includes('"')||t.includes(`
3
3
  `)?`"${t.replace(/"/g,'""')}"`:t}function c(e,t){const n=URL.createObjectURL(t),o=document.createElement("a");o.href=n,o.download=e,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(n)}function h(){const e=new Date,t=n=>String(n).padStart(2,"0");return`${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}${t(e.getSeconds())}`}export{m as S,b as a,u as d,h as g};
4
- //# sourceMappingURL=export-CbQTOt71.js.map
4
+ //# sourceMappingURL=export-C0mlC4AT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"export-CbQTOt71.js","sources":["../../src/components/SearchInput.tsx","../../src/utils/export.ts"],"sourcesContent":["import { Search, X } from 'lucide-react'\n\ninterface Props {\n value: string\n onChange: (value: string) => void\n placeholder?: string\n className?: string\n}\n\nexport default function SearchInput({ value, onChange, placeholder, className = '' }: Props) {\n return (\n <div className={`relative ${className}`}>\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400\" />\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder || '搜索...'}\n className=\"w-full pl-8 pr-8 py-1.5 text-sm border border-gray-300 rounded-md bg-white focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500\"\n />\n {value && (\n <button\n onClick={() => onChange('')}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n )\n}\n","/**\n * 导出工具函数:JSON / CSV\n */\n\nexport function downloadJSON(filename: string, data: unknown): void {\n const content = JSON.stringify(data, null, 2)\n const blob = new Blob([content], { type: 'application/json' })\n triggerDownload(filename, blob)\n}\n\nexport function downloadCSV<T extends object>(filename: string, rows: T[]): void {\n if (!rows || rows.length === 0) {\n downloadJSON(filename.replace(/\\.csv$/, '.json'), rows)\n return\n }\n\n const headers = Object.keys(rows[0]) as (keyof T & string)[]\n const csv = [\n headers.join(','),\n ...rows.map(row =>\n headers.map(h => escapeCSV(row[h])).join(',')\n ),\n ].join('\\n')\n\n // 添加 BOM 让 Excel 识别 UTF-8\n const blob = new Blob(['' + csv], { type: 'text/csv;charset=utf-8' })\n triggerDownload(filename, blob)\n}\n\nfunction escapeCSV(value: unknown): string {\n if (value === null || value === undefined) return ''\n const str = typeof value === 'object' ? JSON.stringify(value) : String(value)\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`\n }\n return str\n}\n\nfunction triggerDownload(filename: string, blob: Blob): void {\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n}\n\nexport function getTimestamp(): string {\n const now = new Date()\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`\n}\n"],"names":["SearchInput","value","onChange","placeholder","className","jsxs","jsx","Search","e","X","downloadJSON","filename","data","content","blob","triggerDownload","downloadCSV","rows","headers","csv","row","h","escapeCSV","str","url","a","getTimestamp","now","pad"],"mappings":"+FASA,SAAwBA,EAAY,CAAE,MAAAC,EAAO,SAAAC,EAAU,YAAAC,EAAa,UAAAC,EAAY,IAAa,CAC3F,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAW,YAAYD,CAAS,GACnC,SAAA,CAAAE,EAAAA,IAACC,EAAA,CAAO,UAAU,kEAAA,CAAmE,EACrFD,EAAAA,IAAC,QAAA,CACC,KAAK,OACL,MAAAL,EACA,SAAWO,GAAMN,EAASM,EAAE,OAAO,KAAK,EACxC,YAAaL,GAAe,QAC5B,UAAU,0JAAA,CAAA,EAEXF,GACCK,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMJ,EAAS,EAAE,EAC1B,UAAU,8EAEV,SAAAI,EAAAA,IAACG,EAAA,CAAE,UAAU,SAAA,CAAU,CAAA,CAAA,CACzB,EAEJ,CAEJ,CC1BO,SAASC,EAAaC,EAAkBC,EAAqB,CAClE,MAAMC,EAAU,KAAK,UAAUD,EAAM,KAAM,CAAC,EACtCE,EAAO,IAAI,KAAK,CAACD,CAAO,EAAG,CAAE,KAAM,mBAAoB,EAC7DE,EAAgBJ,EAAUG,CAAI,CAChC,CAEO,SAASE,EAA8BL,EAAkBM,EAAiB,CAC/E,GAAI,CAACA,GAAQA,EAAK,SAAW,EAAG,CAC9BP,EAAaC,EAAS,QAAQ,SAAU,OAAO,EAAGM,CAAI,EACtD,MACF,CAEA,MAAMC,EAAU,OAAO,KAAKD,EAAK,CAAC,CAAC,EAC7BE,EAAM,CACVD,EAAQ,KAAK,GAAG,EAChB,GAAGD,EAAK,IAAIG,GACVF,EAAQ,IAAIG,GAAKC,EAAUF,EAAIC,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAA,CAC9C,EACA,KAAK;AAAA,CAAI,EAGLP,EAAO,IAAI,KAAK,CAAC,SAAMK,CAAG,EAAG,CAAE,KAAM,yBAA0B,EACrEJ,EAAgBJ,EAAUG,CAAI,CAChC,CAEA,SAASQ,EAAUrB,EAAwB,CACzC,GAAIA,GAAU,KAA6B,MAAO,GAClD,MAAMsB,EAAM,OAAOtB,GAAU,SAAW,KAAK,UAAUA,CAAK,EAAI,OAAOA,CAAK,EAC5E,OAAIsB,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS;AAAA,CAAI,EACtD,IAAIA,EAAI,QAAQ,KAAM,IAAI,CAAC,IAE7BA,CACT,CAEA,SAASR,EAAgBJ,EAAkBG,EAAkB,CAC3D,MAAMU,EAAM,IAAI,gBAAgBV,CAAI,EAC9BW,EAAI,SAAS,cAAc,GAAG,EACpCA,EAAE,KAAOD,EACTC,EAAE,SAAWd,EACb,SAAS,KAAK,YAAYc,CAAC,EAC3BA,EAAE,MAAA,EACF,SAAS,KAAK,YAAYA,CAAC,EAC3B,IAAI,gBAAgBD,CAAG,CACzB,CAEO,SAASE,GAAuB,CACrC,MAAMC,MAAU,KACVC,EAAO,GAAc,OAAO,CAAC,EAAE,SAAS,EAAG,GAAG,EACpD,MAAO,GAAGD,EAAI,YAAA,CAAa,GAAGC,EAAID,EAAI,SAAA,EAAa,CAAC,CAAC,GAAGC,EAAID,EAAI,SAAS,CAAC,IAAIC,EAAID,EAAI,SAAA,CAAU,CAAC,GAAGC,EAAID,EAAI,WAAA,CAAY,CAAC,GAAGC,EAAID,EAAI,WAAA,CAAY,CAAC,EACnJ"}
1
+ {"version":3,"file":"export-C0mlC4AT.js","sources":["../../src/components/SearchInput.tsx","../../src/utils/export.ts"],"sourcesContent":["import { Search, X } from 'lucide-react'\n\ninterface Props {\n value: string\n onChange: (value: string) => void\n placeholder?: string\n className?: string\n}\n\nexport default function SearchInput({ value, onChange, placeholder, className = '' }: Props) {\n return (\n <div className={`relative ${className}`}>\n <Search className=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400\" />\n <input\n type=\"text\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder || '搜索...'}\n className=\"w-full pl-8 pr-8 py-1.5 text-sm border border-gray-300 rounded-md bg-white focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500\"\n />\n {value && (\n <button\n onClick={() => onChange('')}\n className=\"absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n )\n}\n","/**\n * 导出工具函数:JSON / CSV\n */\n\nexport function downloadJSON(filename: string, data: unknown): void {\n const content = JSON.stringify(data, null, 2)\n const blob = new Blob([content], { type: 'application/json' })\n triggerDownload(filename, blob)\n}\n\nexport function downloadCSV<T extends object>(filename: string, rows: T[]): void {\n if (!rows || rows.length === 0) {\n downloadJSON(filename.replace(/\\.csv$/, '.json'), rows)\n return\n }\n\n const headers = Object.keys(rows[0]) as (keyof T & string)[]\n const csv = [\n headers.join(','),\n ...rows.map(row =>\n headers.map(h => escapeCSV(row[h])).join(',')\n ),\n ].join('\\n')\n\n // 添加 BOM 让 Excel 识别 UTF-8\n const blob = new Blob(['' + csv], { type: 'text/csv;charset=utf-8' })\n triggerDownload(filename, blob)\n}\n\nfunction escapeCSV(value: unknown): string {\n if (value === null || value === undefined) return ''\n const str = typeof value === 'object' ? JSON.stringify(value) : String(value)\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`\n }\n return str\n}\n\nfunction triggerDownload(filename: string, blob: Blob): void {\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filename\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n}\n\nexport function getTimestamp(): string {\n const now = new Date()\n const pad = (n: number) => String(n).padStart(2, '0')\n return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`\n}\n"],"names":["SearchInput","value","onChange","placeholder","className","jsxs","jsx","Search","e","X","downloadJSON","filename","data","content","blob","triggerDownload","downloadCSV","rows","headers","csv","row","h","escapeCSV","str","url","a","getTimestamp","now","pad"],"mappings":"+FASA,SAAwBA,EAAY,CAAE,MAAAC,EAAO,SAAAC,EAAU,YAAAC,EAAa,UAAAC,EAAY,IAAa,CAC3F,OACEC,EAAAA,KAAC,MAAA,CAAI,UAAW,YAAYD,CAAS,GACnC,SAAA,CAAAE,EAAAA,IAACC,EAAA,CAAO,UAAU,kEAAA,CAAmE,EACrFD,EAAAA,IAAC,QAAA,CACC,KAAK,OACL,MAAAL,EACA,SAAWO,GAAMN,EAASM,EAAE,OAAO,KAAK,EACxC,YAAaL,GAAe,QAC5B,UAAU,0JAAA,CAAA,EAEXF,GACCK,EAAAA,IAAC,SAAA,CACC,QAAS,IAAMJ,EAAS,EAAE,EAC1B,UAAU,8EAEV,SAAAI,EAAAA,IAACG,EAAA,CAAE,UAAU,SAAA,CAAU,CAAA,CAAA,CACzB,EAEJ,CAEJ,CC1BO,SAASC,EAAaC,EAAkBC,EAAqB,CAClE,MAAMC,EAAU,KAAK,UAAUD,EAAM,KAAM,CAAC,EACtCE,EAAO,IAAI,KAAK,CAACD,CAAO,EAAG,CAAE,KAAM,mBAAoB,EAC7DE,EAAgBJ,EAAUG,CAAI,CAChC,CAEO,SAASE,EAA8BL,EAAkBM,EAAiB,CAC/E,GAAI,CAACA,GAAQA,EAAK,SAAW,EAAG,CAC9BP,EAAaC,EAAS,QAAQ,SAAU,OAAO,EAAGM,CAAI,EACtD,MACF,CAEA,MAAMC,EAAU,OAAO,KAAKD,EAAK,CAAC,CAAC,EAC7BE,EAAM,CACVD,EAAQ,KAAK,GAAG,EAChB,GAAGD,EAAK,IAAIG,GACVF,EAAQ,IAAIG,GAAKC,EAAUF,EAAIC,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAA,CAC9C,EACA,KAAK;AAAA,CAAI,EAGLP,EAAO,IAAI,KAAK,CAAC,SAAMK,CAAG,EAAG,CAAE,KAAM,yBAA0B,EACrEJ,EAAgBJ,EAAUG,CAAI,CAChC,CAEA,SAASQ,EAAUrB,EAAwB,CACzC,GAAIA,GAAU,KAA6B,MAAO,GAClD,MAAMsB,EAAM,OAAOtB,GAAU,SAAW,KAAK,UAAUA,CAAK,EAAI,OAAOA,CAAK,EAC5E,OAAIsB,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS;AAAA,CAAI,EACtD,IAAIA,EAAI,QAAQ,KAAM,IAAI,CAAC,IAE7BA,CACT,CAEA,SAASR,EAAgBJ,EAAkBG,EAAkB,CAC3D,MAAMU,EAAM,IAAI,gBAAgBV,CAAI,EAC9BW,EAAI,SAAS,cAAc,GAAG,EACpCA,EAAE,KAAOD,EACTC,EAAE,SAAWd,EACb,SAAS,KAAK,YAAYc,CAAC,EAC3BA,EAAE,MAAA,EACF,SAAS,KAAK,YAAYA,CAAC,EAC3B,IAAI,gBAAgBD,CAAG,CACzB,CAEO,SAASE,GAAuB,CACrC,MAAMC,MAAU,KACVC,EAAO,GAAc,OAAO,CAAC,EAAE,SAAS,EAAG,GAAG,EACpD,MAAO,GAAGD,EAAI,YAAA,CAAa,GAAGC,EAAID,EAAI,SAAA,EAAa,CAAC,CAAC,GAAGC,EAAID,EAAI,SAAS,CAAC,IAAIC,EAAID,EAAI,SAAA,CAAU,CAAC,GAAGC,EAAID,EAAI,WAAA,CAAY,CAAC,GAAGC,EAAID,EAAI,WAAA,CAAY,CAAC,EACnJ"}
@@ -0,0 +1,3 @@
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Dashboard-D4j0Zmek.js","assets/react-vendor-CSp-GLFF.js","assets/query-C99w429o.js","assets/react-router-I-HqunH7.js","assets/vendor-CMMjVdZs.js","assets/lucide-Bu44HVAM.js","assets/charts-CLrM0_uM.js","assets/Sessions-o3EXsXz9.js","assets/Drawer-Lo5ihVP-.js","assets/date-fns-CZ_bHujz.js","assets/export-C0mlC4AT.js","assets/syntax-highlighter-44FakypI.js","assets/SessionDetail-BPrPyMNa.js","assets/Events-DBJ1B7OW.js","assets/CodeBlock--H53gk46.js","assets/ExecutionTrace-Du9XADc1.js","assets/Agents-ne5lXc7V.js","assets/MarkdownRenderer-CCIz1MOz.js","assets/auth-Bnf8ZcqN.js","assets/Skills-Czt5mkyc.js","assets/Routing-BNQ09OlH.js","assets/AIConfig-D4VglzCl.js"])))=>i.map(i=>d[i]);
2
+ import{r,j as e,e as b,d as y}from"./react-vendor-CSp-GLFF.js";import{u as v,O as N,L as w,R as _,a as c,N as C,B as k}from"./react-router-I-HqunH7.js";import{Q as E,a as T}from"./query-C99w429o.js";import{_ as m}from"./syntax-highlighter-44FakypI.js";import{d as f}from"./vendor-CMMjVdZs.js";import{X as p,M as O,L as P,a as A,A as S,T as L,B as I,b as R,N as z,C as D,I as F,c as V,d as B,e as M,f as q}from"./lucide-Bu44HVAM.js";(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))o(t);new MutationObserver(t=>{for(const n of t)if(n.type==="childList")for(const s of n.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&o(s)}).observe(document,{childList:!0,subtree:!0});function l(t){const n={};return t.integrity&&(n.integrity=t.integrity),t.referrerPolicy&&(n.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?n.credentials="include":t.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function o(t){if(t.ep)return;t.ep=!0;const n=l(t);fetch(t.href,n)}})();const Q=[{title:"总览",items:[{name:"仪表盘",href:"/dashboard",icon:P}]},{title:"活动",items:[{name:"会话",href:"/sessions",icon:A},{name:"事件",href:"/events",icon:S},{name:"执行追踪",href:"/execution-trace",icon:L}]},{title:"配置",items:[{name:"Agent 管理",href:"/agents",icon:I},{name:"Skill 管理",href:"/skills",icon:R},{name:"Agent 路由",href:"/routing",icon:z},{name:"AI 配置",href:"/ai-config",icon:D}]}];function W(){const i=v(),[a,l]=r.useState(!1),o=t=>e.jsx(e.Fragment,{children:Q.map(n=>e.jsxs("div",{className:"mb-4",children:[e.jsx("div",{className:"px-3 py-2 text-xs font-semibold text-gray-400 uppercase tracking-wider",children:n.title}),n.items.map(s=>{const u=s.icon,d=i.pathname===s.href||i.pathname.startsWith(s.href+"/");return e.jsxs(w,{to:s.href,onClick:t,className:f("group flex items-center px-3 py-2 text-sm font-medium rounded-md mb-0.5",d?"bg-indigo-50 text-indigo-600":"text-gray-700 hover:bg-gray-50"),children:[e.jsx(u,{className:f("mr-3 h-4 w-4",d?"text-indigo-600":"text-gray-400")}),s.name]},s.name)})]},n.title))});return e.jsxs("div",{className:"min-h-screen bg-gray-50",children:[e.jsxs("div",{className:f("fixed inset-0 z-40 lg:hidden",a?"block":"hidden"),children:[e.jsx("div",{className:"fixed inset-0 bg-gray-600 bg-opacity-75",onClick:()=>l(!1)}),e.jsxs("div",{className:"fixed inset-y-0 left-0 flex w-64 flex-col bg-white",children:[e.jsxs("div",{className:"flex items-center justify-between px-4 py-4 border-b",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold text-sm",children:"CF"}),e.jsx("h1",{className:"text-base font-bold text-gray-900",children:"Claude Forge"})]}),e.jsx("button",{onClick:()=>l(!1),className:"text-gray-500",children:e.jsx(p,{className:"h-5 w-5"})})]}),e.jsx("nav",{className:"flex-1 overflow-y-auto px-2 py-4",children:o(()=>l(!1))})]})]}),e.jsx("div",{className:"hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col",children:e.jsxs("div",{className:"flex flex-col flex-grow border-r border-gray-200 bg-white",children:[e.jsxs("div",{className:"flex items-center gap-2 px-4 py-4 border-b",children:[e.jsx("div",{className:"w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold text-sm",children:"CF"}),e.jsx("h1",{className:"text-base font-bold text-gray-900",children:"Claude Forge"})]}),e.jsx("nav",{className:"flex-1 overflow-y-auto px-2 py-4",children:o()})]})}),e.jsxs("div",{className:"lg:pl-64",children:[e.jsxs("div",{className:"sticky top-0 z-10 flex h-14 flex-shrink-0 bg-white border-b border-gray-200 lg:hidden",children:[e.jsx("button",{onClick:()=>l(!0),className:"px-4 text-gray-500 focus:outline-none",children:e.jsx(O,{className:"h-5 w-5"})}),e.jsx("div",{className:"flex flex-1 items-center px-4",children:e.jsx("h1",{className:"text-base font-semibold text-gray-900",children:"Claude Forge"})})]}),e.jsx("main",{className:"py-6",children:e.jsx("div",{className:"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8",children:e.jsx(N,{})})})]})]})}const X=r.lazy(()=>m(()=>import("./Dashboard-D4j0Zmek.js"),__vite__mapDeps([0,1,2,3,4,5,6]))),$=r.lazy(()=>m(()=>import("./Sessions-o3EXsXz9.js"),__vite__mapDeps([7,1,2,8,4,5,9,10,3,11]))),K=r.lazy(()=>m(()=>import("./SessionDetail-BPrPyMNa.js"),__vite__mapDeps([12,1,3,4,2,5,9]))),U=r.lazy(()=>m(()=>import("./Events-DBJ1B7OW.js"),__vite__mapDeps([13,1,2,4,8,5,14,11,10,9,3]))),G=r.lazy(()=>m(()=>import("./ExecutionTrace-Du9XADc1.js"),__vite__mapDeps([15,1,2,4,8,5,14,11,10,9,3]))),H=r.lazy(()=>m(()=>import("./Agents-ne5lXc7V.js"),__vite__mapDeps([16,1,2,8,4,5,17,11,18,3]))),J=r.lazy(()=>m(()=>import("./Skills-Czt5mkyc.js"),__vite__mapDeps([19,1,2,8,4,5,17,11,18,3]))),Y=r.lazy(()=>m(()=>import("./Routing-BNQ09OlH.js"),__vite__mapDeps([20,1,2,3,4,5]))),Z=r.lazy(()=>m(()=>import("./AIConfig-D4VglzCl.js"),__vite__mapDeps([21,1,2,18,5,3,4,11])));function x(){return e.jsx("div",{className:"flex items-center justify-center h-full py-16",children:e.jsxs("div",{className:"flex items-center gap-3 text-sm text-gray-500",children:[e.jsx("div",{className:"w-4 h-4 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin"}),"加载中..."]})})}function ee(){return e.jsx(_,{children:e.jsxs(c,{path:"/",element:e.jsx(W,{}),children:[e.jsx(c,{index:!0,element:e.jsx(C,{to:"/dashboard",replace:!0})}),e.jsx(c,{path:"dashboard",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(X,{})})}),e.jsx(c,{path:"sessions",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx($,{})})}),e.jsx(c,{path:"sessions/:id",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(K,{})})}),e.jsx(c,{path:"events",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(U,{})})}),e.jsx(c,{path:"execution-trace",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(G,{})})}),e.jsx(c,{path:"agents",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(H,{})})}),e.jsx(c,{path:"skills",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(J,{})})}),e.jsx(c,{path:"routing",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(Y,{})})}),e.jsx(c,{path:"ai-config",element:e.jsx(r.Suspense,{fallback:e.jsx(x,{}),children:e.jsx(Z,{})})})]})})}const g=r.createContext(null);function ue(){const i=r.useContext(g);if(!i)throw new Error("useToast must be used within ToastProvider");return i}let se=1;function te({children:i}){const[a,l]=r.useState([]),o=r.useCallback(s=>{l(u=>u.filter(d=>d.id!==s))},[]),t=r.useCallback((s,u)=>{const d=se++;l(h=>[...h,{id:d,type:s,message:u}]),setTimeout(()=>o(d),4e3)},[o]),n={show:t,success:s=>t("success",s),error:s=>t("error",s),warning:s=>t("warning",s),info:s=>t("info",s)};return e.jsxs(g.Provider,{value:n,children:[i,e.jsx("div",{className:"fixed top-4 right-4 z-[9999] space-y-2 max-w-md",children:a.map(s=>e.jsx(re,{toast:s,onClose:()=>o(s.id)},s.id))})]})}function re({toast:i,onClose:a}){const l={success:e.jsx(M,{className:"h-5 w-5 text-green-500"}),error:e.jsx(B,{className:"h-5 w-5 text-red-500"}),warning:e.jsx(V,{className:"h-5 w-5 text-yellow-500"}),info:e.jsx(F,{className:"h-5 w-5 text-blue-500"})},o={success:"border-green-200",error:"border-red-200",warning:"border-yellow-200",info:"border-blue-200"};return e.jsxs("div",{className:f("bg-white shadow-lg rounded-lg border px-4 py-3 flex items-start gap-3 animate-in slide-in-from-right",o[i.type]),children:[l[i.type],e.jsx("div",{className:"flex-1 text-sm text-gray-800",children:i.message}),e.jsx("button",{onClick:a,className:"text-gray-400 hover:text-gray-600",children:e.jsx(p,{className:"h-4 w-4"})})]})}const ne=r.createContext(null);function ae({children:i}){var d;const[a,l]=r.useState({open:!1,options:null,resolve:null}),o=r.useCallback(h=>new Promise(j=>{l({open:!0,options:h,resolve:j})}),[]),t=h=>{a.resolve&&a.resolve(h),l({open:!1,options:null,resolve:null})},n=((d=a.options)==null?void 0:d.variant)||"danger",s={danger:"text-red-600",warning:"text-yellow-600",info:"text-blue-600"}[n],u={danger:"bg-red-600 hover:bg-red-700",warning:"bg-yellow-600 hover:bg-yellow-700",info:"bg-indigo-600 hover:bg-indigo-700"}[n];return e.jsxs(ne.Provider,{value:{confirm:o},children:[i,a.open&&a.options&&e.jsxs("div",{className:"fixed inset-0 z-[9999] flex items-center justify-center",children:[e.jsx("div",{className:"absolute inset-0 bg-black bg-opacity-50",onClick:()=>t(!1)}),e.jsxs("div",{className:"relative bg-white rounded-lg shadow-xl max-w-md w-full mx-4 p-6",children:[e.jsxs("div",{className:"flex items-start gap-4",children:[e.jsx("div",{className:`flex-shrink-0 ${s}`,children:e.jsx(q,{className:"h-6 w-6"})}),e.jsxs("div",{className:"flex-1",children:[e.jsx("h3",{className:"text-lg font-semibold text-gray-900 mb-2",children:a.options.title}),e.jsx("p",{className:"text-sm text-gray-600 whitespace-pre-wrap",children:a.options.message})]})]}),e.jsxs("div",{className:"flex justify-end gap-2 mt-6",children:[e.jsx("button",{onClick:()=>t(!1),className:"px-4 py-2 text-sm text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50",children:a.options.cancelText||"取消"}),e.jsx("button",{onClick:()=>t(!0),className:`px-4 py-2 text-sm text-white rounded ${u}`,children:a.options.confirmText||"确定"})]})]})]})]})}const ie=new E({defaultOptions:{queries:{refetchOnWindowFocus:!1,retry:1}}});b.createRoot(document.getElementById("root")).render(e.jsx(y.StrictMode,{children:e.jsx(T,{client:ie,children:e.jsx(te,{children:e.jsx(ae,{children:e.jsx(k,{children:e.jsx(ee,{})})})})})}));export{ue as u};
3
+ //# sourceMappingURL=index-B1J7nBu0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"mappings":"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","names":["sections","LayoutDashboard","MessageSquare","Activity","TrendingUp","Bot","BookOpen","Network","Cpu","Layout","location","useLocation","sidebarOpen","setSidebarOpen","useState","renderNav","onClick","jsx","Fragment","section","jsxs","item","Icon","isActive","Link","clsx","X","Menu","Outlet","Dashboard","lazy","Sessions","SessionDetail","Events","ExecutionTrace","Agents","Skills","Routing","AIConfig","PageFallback","App","Routes","Route","Navigate","Suspense","ToastContext","createContext","useToast","ctx","useContext","nextId","ToastProvider","children","toasts","setToasts","remove","useCallback","id","prev","t","show","type","message","value","msg","ToastItem","toast","onClose","icons","CheckCircle","XCircle","AlertCircle","Info","borderColors","ConfirmContext","ConfirmProvider","state","setState","confirm","options","resolve","handleClose","result","variant","_a","iconColor","btnColor","AlertTriangle","queryClient","QueryClient","ReactDOM","React","QueryClientProvider","BrowserRouter"],"ignoreList":[],"sources":["../../src/components/Layout.tsx","../../src/App.tsx","../../src/components/Toast.tsx","../../src/components/Confirm.tsx","../../src/main.tsx"],"sourcesContent":["import { Outlet, Link, useLocation } from 'react-router-dom'\nimport {\n LayoutDashboard,\n MessageSquare,\n Activity,\n Menu,\n X,\n Bot,\n BookOpen,\n Network,\n Cpu,\n TrendingUp,\n} from 'lucide-react'\nimport { useState } from 'react'\nimport clsx from 'clsx'\n\ninterface NavItem {\n name: string\n href: string\n icon: any\n}\n\ninterface NavSection {\n title: string\n items: NavItem[]\n}\n\nconst sections: NavSection[] = [\n {\n title: '总览',\n items: [\n { name: '仪表盘', href: '/dashboard', icon: LayoutDashboard },\n ],\n },\n {\n title: '活动',\n items: [\n { name: '会话', href: '/sessions', icon: MessageSquare },\n { name: '事件', href: '/events', icon: Activity },\n { name: '执行追踪', href: '/execution-trace', icon: TrendingUp },\n ],\n },\n {\n title: '配置',\n items: [\n { name: 'Agent 管理', href: '/agents', icon: Bot },\n { name: 'Skill 管理', href: '/skills', icon: BookOpen },\n { name: 'Agent 路由', href: '/routing', icon: Network },\n { name: 'AI 配置', href: '/ai-config', icon: Cpu },\n ],\n },\n]\n\nexport default function Layout() {\n const location = useLocation()\n const [sidebarOpen, setSidebarOpen] = useState(false)\n\n const renderNav = (onClick?: () => void) => (\n <>\n {sections.map((section) => (\n <div key={section.title} className=\"mb-4\">\n <div className=\"px-3 py-2 text-xs font-semibold text-gray-400 uppercase tracking-wider\">\n {section.title}\n </div>\n {section.items.map((item) => {\n const Icon = item.icon\n const isActive = location.pathname === item.href || location.pathname.startsWith(item.href + '/')\n return (\n <Link\n key={item.name}\n to={item.href}\n onClick={onClick}\n className={clsx(\n 'group flex items-center px-3 py-2 text-sm font-medium rounded-md mb-0.5',\n isActive\n ? 'bg-indigo-50 text-indigo-600'\n : 'text-gray-700 hover:bg-gray-50'\n )}\n >\n <Icon className={clsx('mr-3 h-4 w-4', isActive ? 'text-indigo-600' : 'text-gray-400')} />\n {item.name}\n </Link>\n )\n })}\n </div>\n ))}\n </>\n )\n\n return (\n <div className=\"min-h-screen bg-gray-50\">\n {/* Mobile sidebar */}\n <div className={clsx('fixed inset-0 z-40 lg:hidden', sidebarOpen ? 'block' : 'hidden')}>\n <div className=\"fixed inset-0 bg-gray-600 bg-opacity-75\" onClick={() => setSidebarOpen(false)} />\n <div className=\"fixed inset-y-0 left-0 flex w-64 flex-col bg-white\">\n <div className=\"flex items-center justify-between px-4 py-4 border-b\">\n <div className=\"flex items-center gap-2\">\n <div className=\"w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold text-sm\">\n CF\n </div>\n <h1 className=\"text-base font-bold text-gray-900\">Claude Forge</h1>\n </div>\n <button onClick={() => setSidebarOpen(false)} className=\"text-gray-500\">\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n <nav className=\"flex-1 overflow-y-auto px-2 py-4\">\n {renderNav(() => setSidebarOpen(false))}\n </nav>\n </div>\n </div>\n\n {/* Desktop sidebar */}\n <div className=\"hidden lg:fixed lg:inset-y-0 lg:flex lg:w-64 lg:flex-col\">\n <div className=\"flex flex-col flex-grow border-r border-gray-200 bg-white\">\n <div className=\"flex items-center gap-2 px-4 py-4 border-b\">\n <div className=\"w-8 h-8 rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold text-sm\">\n CF\n </div>\n <h1 className=\"text-base font-bold text-gray-900\">Claude Forge</h1>\n </div>\n <nav className=\"flex-1 overflow-y-auto px-2 py-4\">{renderNav()}</nav>\n </div>\n </div>\n\n {/* Main content */}\n <div className=\"lg:pl-64\">\n <div className=\"sticky top-0 z-10 flex h-14 flex-shrink-0 bg-white border-b border-gray-200 lg:hidden\">\n <button\n onClick={() => setSidebarOpen(true)}\n className=\"px-4 text-gray-500 focus:outline-none\"\n >\n <Menu className=\"h-5 w-5\" />\n </button>\n <div className=\"flex flex-1 items-center px-4\">\n <h1 className=\"text-base font-semibold text-gray-900\">Claude Forge</h1>\n </div>\n </div>\n <main className=\"py-6\">\n <div className=\"mx-auto max-w-7xl px-4 sm:px-6 lg:px-8\">\n <Outlet />\n </div>\n </main>\n </div>\n </div>\n )\n}\n","import { lazy, Suspense } from 'react'\nimport { Routes, Route, Navigate } from 'react-router-dom'\nimport Layout from './components/Layout'\n\n// 路由级代码分割:按需加载 page 组件,缩小主 bundle\nconst Dashboard = lazy(() => import('./pages/Dashboard'))\nconst Sessions = lazy(() => import('./pages/Sessions'))\nconst SessionDetail = lazy(() => import('./pages/SessionDetail'))\nconst Events = lazy(() => import('./pages/Events'))\nconst ExecutionTrace = lazy(() => import('./pages/ExecutionTrace'))\nconst Agents = lazy(() => import('./pages/Agents'))\nconst Skills = lazy(() => import('./pages/Skills'))\nconst Routing = lazy(() => import('./pages/Routing'))\nconst AIConfig = lazy(() => import('./pages/AIConfig'))\n\nfunction PageFallback() {\n return (\n <div className=\"flex items-center justify-center h-full py-16\">\n <div className=\"flex items-center gap-3 text-sm text-gray-500\">\n <div className=\"w-4 h-4 border-2 border-gray-300 border-t-blue-500 rounded-full animate-spin\" />\n 加载中...\n </div>\n </div>\n )\n}\n\nexport default function App() {\n return (\n <Routes>\n <Route path=\"/\" element={<Layout />}>\n <Route\n index\n element={<Navigate to=\"/dashboard\" replace />}\n />\n <Route\n path=\"dashboard\"\n element={\n <Suspense fallback={<PageFallback />}>\n <Dashboard />\n </Suspense>\n }\n />\n <Route\n path=\"sessions\"\n element={\n <Suspense fallback={<PageFallback />}>\n <Sessions />\n </Suspense>\n }\n />\n <Route\n path=\"sessions/:id\"\n element={\n <Suspense fallback={<PageFallback />}>\n <SessionDetail />\n </Suspense>\n }\n />\n <Route\n path=\"events\"\n element={\n <Suspense fallback={<PageFallback />}>\n <Events />\n </Suspense>\n }\n />\n <Route\n path=\"execution-trace\"\n element={\n <Suspense fallback={<PageFallback />}>\n <ExecutionTrace />\n </Suspense>\n }\n />\n <Route\n path=\"agents\"\n element={\n <Suspense fallback={<PageFallback />}>\n <Agents />\n </Suspense>\n }\n />\n <Route\n path=\"skills\"\n element={\n <Suspense fallback={<PageFallback />}>\n <Skills />\n </Suspense>\n }\n />\n <Route\n path=\"routing\"\n element={\n <Suspense fallback={<PageFallback />}>\n <Routing />\n </Suspense>\n }\n />\n <Route\n path=\"ai-config\"\n element={\n <Suspense fallback={<PageFallback />}>\n <AIConfig />\n </Suspense>\n }\n />\n </Route>\n </Routes>\n )\n}\n","import { createContext, useContext, useState, useCallback, ReactNode } from 'react'\nimport { CheckCircle, XCircle, AlertCircle, Info, X } from 'lucide-react'\nimport clsx from 'clsx'\n\ntype ToastType = 'success' | 'error' | 'warning' | 'info'\n\ninterface Toast {\n id: number\n type: ToastType\n message: string\n}\n\ninterface ToastContextValue {\n show: (type: ToastType, message: string) => void\n success: (message: string) => void\n error: (message: string) => void\n warning: (message: string) => void\n info: (message: string) => void\n}\n\nconst ToastContext = createContext<ToastContextValue | null>(null)\n\nexport function useToast() {\n const ctx = useContext(ToastContext)\n if (!ctx) throw new Error('useToast must be used within ToastProvider')\n return ctx\n}\n\nlet nextId = 1\n\nexport function ToastProvider({ children }: { children: ReactNode }) {\n const [toasts, setToasts] = useState<Toast[]>([])\n\n const remove = useCallback((id: number) => {\n setToasts(prev => prev.filter(t => t.id !== id))\n }, [])\n\n const show = useCallback((type: ToastType, message: string) => {\n const id = nextId++\n setToasts(prev => [...prev, { id, type, message }])\n setTimeout(() => remove(id), 4000)\n }, [remove])\n\n const value: ToastContextValue = {\n show,\n success: (msg) => show('success', msg),\n error: (msg) => show('error', msg),\n warning: (msg) => show('warning', msg),\n info: (msg) => show('info', msg),\n }\n\n return (\n <ToastContext.Provider value={value}>\n {children}\n <div className=\"fixed top-4 right-4 z-[9999] space-y-2 max-w-md\">\n {toasts.map(t => (\n <ToastItem key={t.id} toast={t} onClose={() => remove(t.id)} />\n ))}\n </div>\n </ToastContext.Provider>\n )\n}\n\nfunction ToastItem({ toast, onClose }: { toast: Toast; onClose: () => void }) {\n const icons = {\n success: <CheckCircle className=\"h-5 w-5 text-green-500\" />,\n error: <XCircle className=\"h-5 w-5 text-red-500\" />,\n warning: <AlertCircle className=\"h-5 w-5 text-yellow-500\" />,\n info: <Info className=\"h-5 w-5 text-blue-500\" />,\n }\n const borderColors = {\n success: 'border-green-200',\n error: 'border-red-200',\n warning: 'border-yellow-200',\n info: 'border-blue-200',\n }\n\n return (\n <div\n className={clsx(\n 'bg-white shadow-lg rounded-lg border px-4 py-3 flex items-start gap-3 animate-in slide-in-from-right',\n borderColors[toast.type]\n )}\n >\n {icons[toast.type]}\n <div className=\"flex-1 text-sm text-gray-800\">{toast.message}</div>\n <button onClick={onClose} className=\"text-gray-400 hover:text-gray-600\">\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n )\n}\n","import { createContext, useContext, useState, useCallback, ReactNode } from 'react'\nimport { AlertTriangle } from 'lucide-react'\n\ninterface ConfirmOptions {\n title: string\n message: string\n confirmText?: string\n cancelText?: string\n variant?: 'danger' | 'warning' | 'info'\n}\n\ninterface ConfirmContextValue {\n confirm: (options: ConfirmOptions) => Promise<boolean>\n}\n\nconst ConfirmContext = createContext<ConfirmContextValue | null>(null)\n\nexport function useConfirm() {\n const ctx = useContext(ConfirmContext)\n if (!ctx) throw new Error('useConfirm must be used within ConfirmProvider')\n return ctx.confirm\n}\n\nexport function ConfirmProvider({ children }: { children: ReactNode }) {\n const [state, setState] = useState<{\n open: boolean\n options: ConfirmOptions | null\n resolve: ((value: boolean) => void) | null\n }>({ open: false, options: null, resolve: null })\n\n const confirm = useCallback((options: ConfirmOptions): Promise<boolean> => {\n return new Promise((resolve) => {\n setState({ open: true, options, resolve })\n })\n }, [])\n\n const handleClose = (result: boolean) => {\n if (state.resolve) state.resolve(result)\n setState({ open: false, options: null, resolve: null })\n }\n\n const variant = state.options?.variant || 'danger'\n const iconColor = {\n danger: 'text-red-600',\n warning: 'text-yellow-600',\n info: 'text-blue-600',\n }[variant]\n const btnColor = {\n danger: 'bg-red-600 hover:bg-red-700',\n warning: 'bg-yellow-600 hover:bg-yellow-700',\n info: 'bg-indigo-600 hover:bg-indigo-700',\n }[variant]\n\n return (\n <ConfirmContext.Provider value={{ confirm }}>\n {children}\n {state.open && state.options && (\n <div className=\"fixed inset-0 z-[9999] flex items-center justify-center\">\n <div\n className=\"absolute inset-0 bg-black bg-opacity-50\"\n onClick={() => handleClose(false)}\n />\n <div className=\"relative bg-white rounded-lg shadow-xl max-w-md w-full mx-4 p-6\">\n <div className=\"flex items-start gap-4\">\n <div className={`flex-shrink-0 ${iconColor}`}>\n <AlertTriangle className=\"h-6 w-6\" />\n </div>\n <div className=\"flex-1\">\n <h3 className=\"text-lg font-semibold text-gray-900 mb-2\">\n {state.options.title}\n </h3>\n <p className=\"text-sm text-gray-600 whitespace-pre-wrap\">\n {state.options.message}\n </p>\n </div>\n </div>\n <div className=\"flex justify-end gap-2 mt-6\">\n <button\n onClick={() => handleClose(false)}\n className=\"px-4 py-2 text-sm text-gray-700 bg-white border border-gray-300 rounded hover:bg-gray-50\"\n >\n {state.options.cancelText || '取消'}\n </button>\n <button\n onClick={() => handleClose(true)}\n className={`px-4 py-2 text-sm text-white rounded ${btnColor}`}\n >\n {state.options.confirmText || '确定'}\n </button>\n </div>\n </div>\n </div>\n )}\n </ConfirmContext.Provider>\n )\n}\n","import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { BrowserRouter } from 'react-router-dom'\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query'\nimport App from './App'\nimport { ToastProvider } from './components/Toast'\nimport { ConfirmProvider } from './components/Confirm'\nimport './index.css'\n\nconst queryClient = new QueryClient({\n defaultOptions: {\n queries: {\n refetchOnWindowFocus: false,\n retry: 1,\n },\n },\n})\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <QueryClientProvider client={queryClient}>\n <ToastProvider>\n <ConfirmProvider>\n <BrowserRouter>\n <App />\n </BrowserRouter>\n </ConfirmProvider>\n </ToastProvider>\n </QueryClientProvider>\n </React.StrictMode>,\n)\n"],"file":"assets/index-B1J7nBu0.js"}
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.left-0{left:0}.left-2\.5{left:.625rem}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[9999\]{z-index:9999}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-4{margin-left:1rem}.ml-8{margin-left:2rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.block{display:block}.\!inline{display:inline!important}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-60{height:15rem}.h-8{height:2rem}.h-full{height:100%}.max-h-\[500px\]{max-height:500px}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-8{width:2rem}.w-\[600px\]{width:600px}.w-\[800px\]{width:800px}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-full{--tw-translate-x: 100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(229 231 235 / var(--tw-divide-opacity, 1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-gray-100{--tw-border-opacity: 1;border-color:rgb(243 244 246 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-green-200{--tw-border-opacity: 1;border-color:rgb(187 247 208 / var(--tw-border-opacity, 1))}.border-indigo-200{--tw-border-opacity: 1;border-color:rgb(199 210 254 / var(--tw-border-opacity, 1))}.border-orange-200{--tw-border-opacity: 1;border-color:rgb(254 215 170 / var(--tw-border-opacity, 1))}.border-red-200{--tw-border-opacity: 1;border-color:rgb(254 202 202 / var(--tw-border-opacity, 1))}.border-yellow-200{--tw-border-opacity: 1;border-color:rgb(254 240 138 / var(--tw-border-opacity, 1))}.border-t-blue-500{--tw-border-opacity: 1;border-top-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-gray-600{--tw-bg-opacity: 1;background-color:rgb(75 85 99 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-50{--tw-bg-opacity: 1;background-color:rgb(240 253 244 / var(--tw-bg-opacity, 1))}.bg-indigo-100{--tw-bg-opacity: 1;background-color:rgb(224 231 255 / var(--tw-bg-opacity, 1))}.bg-indigo-50{--tw-bg-opacity: 1;background-color:rgb(238 242 255 / var(--tw-bg-opacity, 1))}.bg-indigo-600{--tw-bg-opacity: 1;background-color:rgb(79 70 229 / var(--tw-bg-opacity, 1))}.bg-orange-100{--tw-bg-opacity: 1;background-color:rgb(255 237 213 / var(--tw-bg-opacity, 1))}.bg-purple-100{--tw-bg-opacity: 1;background-color:rgb(243 232 255 / var(--tw-bg-opacity, 1))}.bg-purple-50{--tw-bg-opacity: 1;background-color:rgb(250 245 255 / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-50{--tw-bg-opacity: 1;background-color:rgb(254 242 242 / var(--tw-bg-opacity, 1))}.bg-red-600{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-100{--tw-bg-opacity: 1;background-color:rgb(254 249 195 / var(--tw-bg-opacity, 1))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.bg-opacity-0{--tw-bg-opacity: 0}.bg-opacity-50{--tw-bg-opacity: .5}.bg-opacity-75{--tw-bg-opacity: .75}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-indigo-500{--tw-gradient-from: #6366f1 var(--tw-gradient-from-position);--tw-gradient-to: rgb(99 102 241 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-purple-600{--tw-gradient-to: #9333ea var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.pr-10{padding-right:2.5rem}.pr-8{padding-right:2rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tracking-wider{letter-spacing:.05em}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-800{--tw-text-opacity: 1;color:rgb(31 41 55 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity: 1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-green-800{--tw-text-opacity: 1;color:rgb(22 101 52 / var(--tw-text-opacity, 1))}.text-indigo-600{--tw-text-opacity: 1;color:rgb(79 70 229 / var(--tw-text-opacity, 1))}.text-indigo-700{--tw-text-opacity: 1;color:rgb(67 56 202 / var(--tw-text-opacity, 1))}.text-orange-800{--tw-text-opacity: 1;color:rgb(154 52 18 / var(--tw-text-opacity, 1))}.text-purple-600{--tw-text-opacity: 1;color:rgb(147 51 234 / var(--tw-text-opacity, 1))}.text-purple-700{--tw-text-opacity: 1;color:rgb(126 34 206 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-red-800{--tw-text-opacity: 1;color:rgb(153 27 27 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.text-yellow-600{--tw-text-opacity: 1;color:rgb(202 138 4 / var(--tw-text-opacity, 1))}.text-yellow-700{--tw-text-opacity: 1;color:rgb(161 98 7 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}:root{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,sans-serif;line-height:1.5;font-weight:400;color-scheme:light;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;min-height:100vh}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-200:hover{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-indigo-700:hover{--tw-bg-opacity: 1;background-color:rgb(67 56 202 / var(--tw-bg-opacity, 1))}.hover\:bg-red-700:hover{--tw-bg-opacity: 1;background-color:rgb(185 28 28 / var(--tw-bg-opacity, 1))}.hover\:bg-yellow-700:hover{--tw-bg-opacity: 1;background-color:rgb(161 98 7 / var(--tw-bg-opacity, 1))}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.hover\:text-gray-700:hover{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.hover\:text-gray-900:hover{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.hover\:text-indigo-700:hover{--tw-text-opacity: 1;color:rgb(67 56 202 / var(--tw-text-opacity, 1))}.hover\:shadow-md:hover{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.focus\:border-indigo-500:focus{--tw-border-opacity: 1;border-color:rgb(99 102 241 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-indigo-500:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(99 102 241 / var(--tw-ring-opacity, 1))}.disabled\:bg-gray-400:disabled{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.disabled\:bg-gray-50:disabled{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.disabled\:text-gray-400:disabled{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width: 640px){.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:fixed{position:fixed}.lg\:inset-y-0{top:0;bottom:0}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:w-64{width:16rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:flex-col{flex-direction:column}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:pl-64{padding-left:16rem}}