From 6be88f4178d059229c56290778d92851cdcb5912 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sat, 4 Apr 2026 07:47:02 +0700 Subject: [PATCH] =?UTF-8?q?refactor(desktop):=20react-arch=20audit=20?= =?UTF-8?q?=E2=80=94=20schemas,=20RHF=20forms,=20file=20splits,=20services?= =?UTF-8?q?,=20store=20cleanup,=20dir=20restructure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Phase 1: add zod, react-hook-form, @hookform/resolvers; create 8 Zod schemas - Phase 2: migrate 6 form dialogs from useState to useForm+zodResolver - Phase 3: split 24 files >200L into ~40 focused modules (0 files >300L remaining) - Phase 4: create services/ layer (8 service files), eliminate direct ws.call in components - Phase 5: split chat-store into chat-message-store + chat-activity-store - Phase 6: flatten settings/ directory — move 9 feature subdirs to top-level components/ --- ui/desktop/frontend/package.json | 3 + ui/desktop/frontend/pnpm-lock.yaml | 39 ++ ui/desktop/frontend/src/App.tsx | 6 +- .../{settings => }/agents/AgentCard.tsx | 2 +- .../agents/AgentDetailPanel.tsx | 4 +- .../{settings => }/agents/AgentFilesTab.tsx | 103 +---- .../src/components/agents/AgentFormDialog.tsx | 194 ++++++++++ .../{settings => }/agents/AgentList.tsx | 10 +- .../{settings => }/agents/AgentMcpSection.tsx | 6 +- .../agents/AgentSkillsSection.tsx | 4 +- .../agents/EvolutionSection.tsx | 2 +- .../{settings => }/agents/MemorySection.tsx | 2 +- .../agents/ModelBudgetSection.tsx | 6 +- .../agents/PersonalitySection.tsx | 4 +- .../agents/agent-identity-fields.tsx | 74 ++++ .../agents/agent-preset-selector.tsx | 44 +++ .../agents/agent-regenerate-dialog.tsx | 73 ++++ .../channels/ChannelAdvancedTab.tsx | 2 +- .../{settings => }/channels/ChannelCard.tsx | 6 +- .../channels/ChannelCredentialsTab.tsx | 2 +- .../channels/ChannelDetailPanel.tsx | 8 +- .../channels/ChannelFormDialog.tsx | 98 +++-- .../channels/ChannelGeneralTab.tsx | 8 +- .../{settings => }/channels/ChannelList.tsx | 12 +- .../channels/ChannelManagersTab.tsx | 107 ++---- .../channels/PairedDevicesSection.tsx | 8 +- .../channels/channel-field-renderer.tsx | 4 +- .../channels/channel-schemas.ts | 0 .../channels/manager-group-card.tsx | 117 ++++++ .../src/components/chat/FilePreviewDialog.tsx | 137 +------ .../frontend/src/components/chat/InputBar.tsx | 143 +------ .../src/components/chat/MessageBubble.tsx | 2 +- .../src/components/chat/TaskPanel.tsx | 4 +- .../chat/attachment-preview-strip.tsx | 44 +++ .../components/chat/file-preview-renderer.tsx | 130 +++++++ .../components/chat/use-file-attachments.ts | 74 ++++ .../{settings => }/cron/CronFormDialog.tsx | 160 +++----- .../frontend/src/components/cron/CronList.tsx | 160 ++++++++ .../{settings => }/cron/CronRunsDialog.tsx | 2 +- .../src/components/cron/cron-list-item.tsx | 127 +++++++ .../layout/sidebar/SidebarTeams.tsx | 7 +- .../src/components/mcp/McpFormDialog.tsx | 219 +++++++++++ .../{settings => }/mcp/McpGrantsDialog.tsx | 6 +- .../src/components/mcp/McpServerList.tsx | 158 ++++++++ .../{settings => }/mcp/McpToolsDialog.tsx | 2 +- .../src/components/mcp/mcp-server-row.tsx | 112 ++++++ .../src/components/mcp/mcp-test-result.tsx | 35 ++ .../components/mcp/mcp-transport-fields.tsx | 57 +++ .../components/onboarding/SummoningModal.tsx | 59 +-- .../onboarding/summoning-progress-steps.tsx | 61 +++ .../providers/ProviderFormDialog.tsx | 103 +++-- .../{settings => }/providers/ProviderList.tsx | 6 +- .../{settings => }/providers/ProviderRow.tsx | 4 +- .../src/components/settings/SettingsView.tsx | 18 +- .../settings/agents/AgentFormDialog.tsx | 333 ----------------- .../src/components/settings/cron/CronList.tsx | 266 ------------- .../components/settings/mcp/McpFormDialog.tsx | 351 ------------------ .../components/settings/mcp/McpServerList.tsx | 250 ------------- .../settings/tools/ToolSettingsDialog.tsx | 250 ------------- .../{settings => }/skills/SkillList.tsx | 107 +----- .../src/components/skills/skill-card.tsx | 86 +++++ .../storage/StorageFileBrowser.tsx | 4 +- .../components/storage/StorageFileTree.tsx | 88 +++++ .../components/storage/StorageFileViewer.tsx | 44 +++ .../{settings => }/storage/StorageTab.tsx | 12 +- .../storage/StorageUploadDialog.tsx | 94 +---- .../file-tree-node.tsx} | 178 +-------- .../file-viewer-content.tsx} | 59 +-- .../storage/upload-progress-list.tsx | 87 +++++ .../components/storage/use-file-tree-dnd.ts | 88 +++++ .../src/components/teams/TaskDetailModal.tsx | 203 +--------- .../src/components/teams/TeamBoard.tsx | 166 ++------- .../src/components/teams/TeamCreateDialog.tsx | 7 +- .../components/teams/TeamSettingsModal.tsx | 254 ++++--------- .../src/components/teams/task-detail-meta.tsx | 197 ++++++++++ .../src/components/teams/team-member-list.tsx | 80 ++++ .../components/teams/team-task-list-view.tsx | 94 +++++ .../tools/MediaProviderChainForm.tsx | 155 +------- .../{settings => }/tools/ToolList.tsx | 8 +- .../components/tools/ToolSettingsDialog.tsx | 53 +++ .../components/tools/extractor-chain-form.tsx | 121 ++++++ .../components/tools/json-settings-form.tsx | 84 +++++ .../tools/sortable-provider-card.tsx | 98 +++++ .../traces/TraceDetailDialog.tsx | 200 +--------- .../{settings => }/traces/TraceList.tsx | 119 +----- .../traces/trace-detail-formatters.ts | 47 +++ .../src/components/traces/trace-list-row.tsx | 80 ++++ .../src/components/traces/trace-span-tree.tsx | 126 +++++++ .../frontend/src/hooks/chat-file-helpers.ts | 23 ++ .../frontend/src/hooks/chat-history-mapper.ts | 73 ++++ .../frontend/src/hooks/team-task-debouncer.ts | 76 ++++ .../frontend/src/hooks/use-agent-crud.ts | 12 +- ui/desktop/frontend/src/hooks/use-agents.ts | 15 +- ui/desktop/frontend/src/hooks/use-chat.ts | 329 +++------------- ui/desktop/frontend/src/hooks/use-cron.ts | 22 +- .../frontend/src/hooks/use-mcp-servers.ts | 28 +- .../frontend/src/hooks/use-providers.ts | 12 +- ui/desktop/frontend/src/hooks/use-sessions.ts | 40 +- ui/desktop/frontend/src/hooks/use-storage.ts | 54 +-- .../frontend/src/hooks/use-stream-batcher.ts | 37 ++ .../frontend/src/hooks/use-team-manage.ts | 15 +- .../frontend/src/hooks/use-team-tasks.ts | 144 ++----- ui/desktop/frontend/src/lib/ws-types.ts | 47 +++ ui/desktop/frontend/src/lib/ws.ts | 55 +-- .../frontend/src/schemas/agent.schema.ts | 17 + .../frontend/src/schemas/channel.schema.ts | 11 + .../frontend/src/schemas/cron.schema.ts | 30 ++ ui/desktop/frontend/src/schemas/mcp.schema.ts | 17 + .../src/schemas/media-chain.schema.ts | 18 + .../frontend/src/schemas/provider.schema.ts | 11 + .../src/schemas/team-settings.schema.ts | 19 + .../src/schemas/tool-settings.schema.ts | 20 + .../frontend/src/services/agent-service.ts | 59 +++ .../frontend/src/services/chat-service.ts | 39 ++ .../frontend/src/services/cron-service.ts | 39 ++ .../frontend/src/services/mcp-service.ts | 54 +++ .../frontend/src/services/provider-service.ts | 37 ++ .../frontend/src/services/session-service.ts | 21 ++ .../frontend/src/services/storage-service.ts | 61 +++ .../frontend/src/services/team-service.ts | 91 +++++ .../src/stores/chat-activity-store.ts | 42 +++ .../frontend/src/stores/chat-message-store.ts | 147 ++++++++ ui/desktop/frontend/src/stores/chat-store.ts | 181 +-------- 123 files changed, 4717 insertions(+), 4346 deletions(-) rename ui/desktop/frontend/src/components/{settings => }/agents/AgentCard.tsx (99%) rename ui/desktop/frontend/src/components/{settings => }/agents/AgentDetailPanel.tsx (98%) rename ui/desktop/frontend/src/components/{settings => }/agents/AgentFilesTab.tsx (60%) create mode 100644 ui/desktop/frontend/src/components/agents/AgentFormDialog.tsx rename ui/desktop/frontend/src/components/{settings => }/agents/AgentList.tsx (93%) rename ui/desktop/frontend/src/components/{settings => }/agents/AgentMcpSection.tsx (96%) rename ui/desktop/frontend/src/components/{settings => }/agents/AgentSkillsSection.tsx (97%) rename ui/desktop/frontend/src/components/{settings => }/agents/EvolutionSection.tsx (96%) rename ui/desktop/frontend/src/components/{settings => }/agents/MemorySection.tsx (98%) rename ui/desktop/frontend/src/components/{settings => }/agents/ModelBudgetSection.tsx (97%) rename ui/desktop/frontend/src/components/{settings => }/agents/PersonalitySection.tsx (97%) create mode 100644 ui/desktop/frontend/src/components/agents/agent-identity-fields.tsx create mode 100644 ui/desktop/frontend/src/components/agents/agent-preset-selector.tsx create mode 100644 ui/desktop/frontend/src/components/agents/agent-regenerate-dialog.tsx rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelAdvancedTab.tsx (98%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelCard.tsx (96%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelCredentialsTab.tsx (96%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelDetailPanel.tsx (95%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelFormDialog.tsx (62%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelGeneralTab.tsx (96%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelList.tsx (92%) rename ui/desktop/frontend/src/components/{settings => }/channels/ChannelManagersTab.tsx (54%) rename ui/desktop/frontend/src/components/{settings => }/channels/PairedDevicesSection.tsx (96%) rename ui/desktop/frontend/src/components/{settings => }/channels/channel-field-renderer.tsx (98%) rename ui/desktop/frontend/src/components/{settings => }/channels/channel-schemas.ts (100%) create mode 100644 ui/desktop/frontend/src/components/channels/manager-group-card.tsx create mode 100644 ui/desktop/frontend/src/components/chat/attachment-preview-strip.tsx create mode 100644 ui/desktop/frontend/src/components/chat/file-preview-renderer.tsx create mode 100644 ui/desktop/frontend/src/components/chat/use-file-attachments.ts rename ui/desktop/frontend/src/components/{settings => }/cron/CronFormDialog.tsx (61%) create mode 100644 ui/desktop/frontend/src/components/cron/CronList.tsx rename ui/desktop/frontend/src/components/{settings => }/cron/CronRunsDialog.tsx (98%) create mode 100644 ui/desktop/frontend/src/components/cron/cron-list-item.tsx create mode 100644 ui/desktop/frontend/src/components/mcp/McpFormDialog.tsx rename ui/desktop/frontend/src/components/{settings => }/mcp/McpGrantsDialog.tsx (97%) create mode 100644 ui/desktop/frontend/src/components/mcp/McpServerList.tsx rename ui/desktop/frontend/src/components/{settings => }/mcp/McpToolsDialog.tsx (98%) create mode 100644 ui/desktop/frontend/src/components/mcp/mcp-server-row.tsx create mode 100644 ui/desktop/frontend/src/components/mcp/mcp-test-result.tsx create mode 100644 ui/desktop/frontend/src/components/mcp/mcp-transport-fields.tsx create mode 100644 ui/desktop/frontend/src/components/onboarding/summoning-progress-steps.tsx rename ui/desktop/frontend/src/components/{settings => }/providers/ProviderFormDialog.tsx (63%) rename ui/desktop/frontend/src/components/{settings => }/providers/ProviderList.tsx (93%) rename ui/desktop/frontend/src/components/{settings => }/providers/ProviderRow.tsx (95%) delete mode 100644 ui/desktop/frontend/src/components/settings/agents/AgentFormDialog.tsx delete mode 100644 ui/desktop/frontend/src/components/settings/cron/CronList.tsx delete mode 100644 ui/desktop/frontend/src/components/settings/mcp/McpFormDialog.tsx delete mode 100644 ui/desktop/frontend/src/components/settings/mcp/McpServerList.tsx delete mode 100644 ui/desktop/frontend/src/components/settings/tools/ToolSettingsDialog.tsx rename ui/desktop/frontend/src/components/{settings => }/skills/SkillList.tsx (60%) create mode 100644 ui/desktop/frontend/src/components/skills/skill-card.tsx rename ui/desktop/frontend/src/components/{settings => }/storage/StorageFileBrowser.tsx (97%) create mode 100644 ui/desktop/frontend/src/components/storage/StorageFileTree.tsx create mode 100644 ui/desktop/frontend/src/components/storage/StorageFileViewer.tsx rename ui/desktop/frontend/src/components/{settings => }/storage/StorageTab.tsx (95%) rename ui/desktop/frontend/src/components/{settings => }/storage/StorageUploadDialog.tsx (63%) rename ui/desktop/frontend/src/components/{settings/storage/StorageFileTree.tsx => storage/file-tree-node.tsx} (60%) rename ui/desktop/frontend/src/components/{settings/storage/StorageFileViewer.tsx => storage/file-viewer-content.tsx} (82%) create mode 100644 ui/desktop/frontend/src/components/storage/upload-progress-list.tsx create mode 100644 ui/desktop/frontend/src/components/storage/use-file-tree-dnd.ts create mode 100644 ui/desktop/frontend/src/components/teams/task-detail-meta.tsx create mode 100644 ui/desktop/frontend/src/components/teams/team-member-list.tsx create mode 100644 ui/desktop/frontend/src/components/teams/team-task-list-view.tsx rename ui/desktop/frontend/src/components/{settings => }/tools/MediaProviderChainForm.tsx (53%) rename ui/desktop/frontend/src/components/{settings => }/tools/ToolList.tsx (97%) create mode 100644 ui/desktop/frontend/src/components/tools/ToolSettingsDialog.tsx create mode 100644 ui/desktop/frontend/src/components/tools/extractor-chain-form.tsx create mode 100644 ui/desktop/frontend/src/components/tools/json-settings-form.tsx create mode 100644 ui/desktop/frontend/src/components/tools/sortable-provider-card.tsx rename ui/desktop/frontend/src/components/{settings => }/traces/TraceDetailDialog.tsx (55%) rename ui/desktop/frontend/src/components/{settings => }/traces/TraceList.tsx (52%) create mode 100644 ui/desktop/frontend/src/components/traces/trace-detail-formatters.ts create mode 100644 ui/desktop/frontend/src/components/traces/trace-list-row.tsx create mode 100644 ui/desktop/frontend/src/components/traces/trace-span-tree.tsx create mode 100644 ui/desktop/frontend/src/hooks/chat-file-helpers.ts create mode 100644 ui/desktop/frontend/src/hooks/chat-history-mapper.ts create mode 100644 ui/desktop/frontend/src/hooks/team-task-debouncer.ts create mode 100644 ui/desktop/frontend/src/hooks/use-stream-batcher.ts create mode 100644 ui/desktop/frontend/src/lib/ws-types.ts create mode 100644 ui/desktop/frontend/src/schemas/agent.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/channel.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/cron.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/mcp.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/media-chain.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/provider.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/team-settings.schema.ts create mode 100644 ui/desktop/frontend/src/schemas/tool-settings.schema.ts create mode 100644 ui/desktop/frontend/src/services/agent-service.ts create mode 100644 ui/desktop/frontend/src/services/chat-service.ts create mode 100644 ui/desktop/frontend/src/services/cron-service.ts create mode 100644 ui/desktop/frontend/src/services/mcp-service.ts create mode 100644 ui/desktop/frontend/src/services/provider-service.ts create mode 100644 ui/desktop/frontend/src/services/session-service.ts create mode 100644 ui/desktop/frontend/src/services/storage-service.ts create mode 100644 ui/desktop/frontend/src/services/team-service.ts create mode 100644 ui/desktop/frontend/src/stores/chat-activity-store.ts create mode 100644 ui/desktop/frontend/src/stores/chat-message-store.ts diff --git a/ui/desktop/frontend/package.json b/ui/desktop/frontend/package.json index 93b00818..f5cf4168 100644 --- a/ui/desktop/frontend/package.json +++ b/ui/desktop/frontend/package.json @@ -12,17 +12,20 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@hookform/resolvers": "^5.2.2", "clsx": "^2.1.1", "framer-motion": "^12.38.0", "i18next": "^26.0.1", "immer": "^11.1.4", "react": "^19.2.4", "react-dom": "^19.2.4", + "react-hook-form": "^7.72.1", "react-i18next": "^17.0.1", "react-markdown": "^10.1.0", "react-syntax-highlighter": "^16.1.1", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", + "zod": "^4.3.6", "zustand": "^5.0.12" }, "devDependencies": { diff --git a/ui/desktop/frontend/pnpm-lock.yaml b/ui/desktop/frontend/pnpm-lock.yaml index 74e95b38..3f4eadc7 100644 --- a/ui/desktop/frontend/pnpm-lock.yaml +++ b/ui/desktop/frontend/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@dnd-kit/utilities': specifier: ^3.2.2 version: 3.2.2(react@19.2.4) + '@hookform/resolvers': + specifier: ^5.2.2 + version: 5.2.2(react-hook-form@7.72.1(react@19.2.4)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -35,6 +38,9 @@ importers: react-dom: specifier: ^19.2.4 version: 19.2.4(react@19.2.4) + react-hook-form: + specifier: ^7.72.1 + version: 7.72.1(react@19.2.4) react-i18next: specifier: ^17.0.1 version: 17.0.1(i18next@26.0.1(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2) @@ -50,6 +56,9 @@ importers: remark-gfm: specifier: ^4.0.1 version: 4.0.1 + zod: + specifier: ^4.3.6 + version: 4.3.6 zustand: specifier: ^5.0.12 version: 5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)) @@ -116,6 +125,11 @@ packages: '@emnapi/wasi-threads@1.2.0': resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@hookform/resolvers@5.2.2': + resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} + peerDependencies: + react-hook-form: ^7.55.0 + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -242,6 +256,9 @@ packages: '@rolldown/pluginutils@1.0.0-rc.7': resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@tailwindcss/node@4.2.2': resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} @@ -812,6 +829,12 @@ packages: peerDependencies: react: ^19.2.4 + react-hook-form@7.72.1: + resolution: {integrity: sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-i18next@17.0.1: resolution: {integrity: sha512-iG65FGnFHcYyHNuT01ukffYWCOBFTWSdVD8EZd/dCVWgtjFPObcSsvYYNwcsokO/rDcTb5d6D8Acv8MrOdm6Hw==} peerDependencies: @@ -987,6 +1010,9 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zustand@5.0.12: resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} engines: {node: '>=12.20.0'} @@ -1053,6 +1079,11 @@ snapshots: tslib: 2.8.1 optional: true + '@hookform/resolvers@5.2.2(react-hook-form@7.72.1(react@19.2.4))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.72.1(react@19.2.4) + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1135,6 +1166,8 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.7': {} + '@standard-schema/utils@0.3.0': {} + '@tailwindcss/node@4.2.2': dependencies: '@jridgewell/remapping': 2.3.5 @@ -1844,6 +1877,10 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 + react-hook-form@7.72.1(react@19.2.4): + dependencies: + react: 19.2.4 + react-i18next@17.0.1(i18next@26.0.1(typescript@6.0.2))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@6.0.2): dependencies: '@babel/runtime': 7.29.2 @@ -2054,6 +2091,8 @@ snapshots: void-elements@3.1.0: {} + zod@4.3.6: {} + zustand@5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4)(use-sync-external-store@1.6.0(react@19.2.4)): optionalDependencies: '@types/react': 19.2.14 diff --git a/ui/desktop/frontend/src/App.tsx b/ui/desktop/frontend/src/App.tsx index 24b2b252..841b1ac9 100644 --- a/ui/desktop/frontend/src/App.tsx +++ b/ui/desktop/frontend/src/App.tsx @@ -8,7 +8,8 @@ import { wails } from './lib/wails' import { initWsClient } from './lib/ws' import { initApiClient } from './lib/api' import { useSessionStore } from './stores/session-store' -import { useChatStore } from './stores/chat-store' +import { useChatMessageStore } from './stores/chat-message-store' +import { useChatActivityStore } from './stores/chat-activity-store' import { ErrorBoundary } from './components/common/ErrorBoundary' import { Toaster } from './components/common/Toaster' import { SplashScreen } from './components/common/SplashScreen' @@ -27,7 +28,8 @@ function AppReady() { e.preventDefault() // "New Chat" — clear session + chat directly (avoids duplicate useSessions instance) useSessionStore.getState().setActiveSession(null) - useChatStore.getState().clear() + useChatMessageStore.getState().clear() + useChatActivityStore.getState().clear() } if (mod && e.key === ',') { e.preventDefault(); openSettings() } if (e.key === 'Escape' && activeView === 'settings') { closeSettings() } diff --git a/ui/desktop/frontend/src/components/settings/agents/AgentCard.tsx b/ui/desktop/frontend/src/components/agents/AgentCard.tsx similarity index 99% rename from ui/desktop/frontend/src/components/settings/agents/AgentCard.tsx rename to ui/desktop/frontend/src/components/agents/AgentCard.tsx index 00b4ffd4..ac5033ee 100644 --- a/ui/desktop/frontend/src/components/settings/agents/AgentCard.tsx +++ b/ui/desktop/frontend/src/components/agents/AgentCard.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next' -import type { AgentData } from '../../../types/agent' +import type { AgentData } from '../../types/agent' const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i diff --git a/ui/desktop/frontend/src/components/settings/agents/AgentDetailPanel.tsx b/ui/desktop/frontend/src/components/agents/AgentDetailPanel.tsx similarity index 98% rename from ui/desktop/frontend/src/components/settings/agents/AgentDetailPanel.tsx rename to ui/desktop/frontend/src/components/agents/AgentDetailPanel.tsx index 6772cc48..fbc1834d 100644 --- a/ui/desktop/frontend/src/components/settings/agents/AgentDetailPanel.tsx +++ b/ui/desktop/frontend/src/components/agents/AgentDetailPanel.tsx @@ -6,8 +6,8 @@ import { EvolutionSection } from './EvolutionSection' import { AgentSkillsSection } from './AgentSkillsSection' import { AgentMcpSection } from './AgentMcpSection' import { AgentFilesTab } from './AgentFilesTab' -import { ConfirmDialog } from '../../common/ConfirmDialog' -import type { AgentData } from '../../../types/agent' +import { ConfirmDialog } from '../common/ConfirmDialog' +import type { AgentData } from '../../types/agent' type DetailTab = 'overview' | 'files' diff --git a/ui/desktop/frontend/src/components/settings/agents/AgentFilesTab.tsx b/ui/desktop/frontend/src/components/agents/AgentFilesTab.tsx similarity index 60% rename from ui/desktop/frontend/src/components/settings/agents/AgentFilesTab.tsx rename to ui/desktop/frontend/src/components/agents/AgentFilesTab.tsx index 28e56369..f4a29e25 100644 --- a/ui/desktop/frontend/src/components/settings/agents/AgentFilesTab.tsx +++ b/ui/desktop/frontend/src/components/agents/AgentFilesTab.tsx @@ -1,8 +1,8 @@ import { useState, useEffect, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { getWsClient } from '../../../lib/ws' -import { getApiClient } from '../../../lib/api' -import type { BootstrapFile } from '../../../types/agent' +import { agentService } from '../../services/agent-service' +import { RegenerateDialog } from './agent-regenerate-dialog' +import type { BootstrapFile } from '../../types/agent' /** Files hidden from desktop UI — USER.md is per-user (managed by bootstrap), HEARTBEAT.md needs cron config */ const HIDDEN_FILES = ['MEMORY.json', 'BOOTSTRAP.md', 'USER.md', 'USER_PREDEFINED.md', 'HEARTBEAT.md'] @@ -24,16 +24,12 @@ export function AgentFilesTab({ agentId, agentKey, agentType }: AgentFilesTabPro const [dirty, setDirty] = useState(false) const [regenerateOpen, setRegenerateOpen] = useState(false) - // Load file list useEffect(() => { - const ws = getWsClient() setLoading(true) - ws.call('agents.files.list', { agentId: agentKey }) - .then((res: unknown) => { - const r = res as { files?: BootstrapFile[] } - const fileList = (r?.files ?? []).filter((f) => !HIDDEN_FILES.includes(f.name)) + agentService.listFiles(agentKey) + .then((res) => { + const fileList = (res?.files ?? []).filter((f) => !HIDDEN_FILES.includes(f.name)) setFiles(fileList) - // Auto-select first file if (fileList.length > 0 && !selectedFile) { setSelectedFile(fileList[0].name) } @@ -42,12 +38,10 @@ export function AgentFilesTab({ agentId, agentKey, agentType }: AgentFilesTabPro .finally(() => setLoading(false)) }, [agentKey, agentType, selectedFile]) - // Load file content const loadFile = useCallback(async (name: string) => { setFileLoading(true) try { - const ws = getWsClient() - const res = (await ws.call('agents.files.get', { agentId: agentKey, name })) as { file?: BootstrapFile } + const res = await agentService.getFile(agentKey, name) setContent(res?.file?.content ?? '') setDirty(false) } catch { @@ -65,8 +59,7 @@ export function AgentFilesTab({ agentId, agentKey, agentType }: AgentFilesTabPro if (!selectedFile) return setSaving(true) try { - const ws = getWsClient() - await ws.call('agents.files.set', { agentId: agentKey, name: selectedFile, content }) + await agentService.setFile(agentKey, selectedFile, content) setDirty(false) } catch (err) { console.error('Failed to save file:', err) @@ -77,8 +70,7 @@ export function AgentFilesTab({ agentId, agentKey, agentType }: AgentFilesTabPro const handleRegenerate = async (prompt: string) => { try { - await getApiClient().post(`/v1/agents/${agentId}/regenerate`, { prompt }) - // Reload current file after regeneration + await agentService.regenerate(agentId, prompt) if (selectedFile) { setTimeout(() => loadFile(selectedFile), 2000) } @@ -176,78 +168,11 @@ export function AgentFilesTab({ agentId, agentKey, agentType }: AgentFilesTabPro - {/* Edit with AI dialog */} - {regenerateOpen && ( - - )} - - ) -} - -/** Dialog for "Edit with AI" — sends prompt to regenerate endpoint */ -function RegenerateDialog({ open, onOpenChange, onRegenerate }: { - open: boolean - onOpenChange: (open: boolean) => void - onRegenerate: (prompt: string) => Promise -}) { - const { t } = useTranslation('agents') - const [prompt, setPrompt] = useState('') - const [loading, setLoading] = useState(false) - - const handleSubmit = async () => { - if (!prompt.trim()) return - setLoading(true) - try { - await onRegenerate(prompt.trim()) - onOpenChange(false) - setPrompt('') - } finally { - setLoading(false) - } - } - - if (!open) return null - - return ( -
-
-

- - - - Edit with AI -

-

- Describe how you want to modify the agent's personality, knowledge, or behavior. The AI will regenerate the context files accordingly. -

-