@nextclaw/ui 0.12.8 → 0.12.10

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 (227) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/dist/assets/ChannelsList-M9FTK1Ak.js +8 -0
  3. package/dist/assets/DocBrowser-CH7-GxlL.js +1 -0
  4. package/dist/assets/{DocBrowser-BMxf9CIK.js → DocBrowser-DMfr0Oow.js} +1 -1
  5. package/dist/assets/{DocBrowserContext-Ce28gRXt.js → DocBrowserContext-BXydqby-.js} +1 -1
  6. package/dist/assets/{LogoBadge-o92MOA2L.js → LogoBadge-hO7tY7hE.js} +1 -1
  7. package/dist/assets/ModelConfig-CNIgLf0e.js +1 -0
  8. package/dist/assets/{ProviderScopedModelInput-CmTIzgI7.js → ProviderScopedModelInput-B3HWP4oz.js} +1 -1
  9. package/dist/assets/ProvidersList-CHjMnRhX.js +1 -0
  10. package/dist/assets/RuntimeConfig-psp8nMSG.js +1 -0
  11. package/dist/assets/SearchConfig-CSoKip1f.js +1 -0
  12. package/dist/assets/{SecretsConfig-Ba1RPJaG.js → SecretsConfig-MEt6MjuD.js} +2 -2
  13. package/dist/assets/SessionsConfig-DifCiXwR.js +2 -0
  14. package/dist/assets/{app-query-client-DniXoIN5.js → app-query-client-9jNewezV.js} +1 -1
  15. package/dist/assets/{book-open-DocgeQtR.js → book-open-DzdUViDm.js} +1 -1
  16. package/dist/assets/chat-page-CLp0UV0Y.js +58 -0
  17. package/dist/assets/chat-session-display-DsYHx0RZ.js +1 -0
  18. package/dist/assets/{chunk-JZWAC4HX-BvKvh1R8.js → chunk-JZWAC4HX-C5dEc8hV.js} +1 -1
  19. package/dist/assets/{client-CVqPF5ie.js → client-C-8fH7-c.js} +1 -1
  20. package/dist/assets/{config-Bop2oB18.js → config-CBScxsdV.js} +1 -1
  21. package/dist/assets/config-split-page-BUout_Ak.js +1 -0
  22. package/dist/assets/{createLucideIcon-DVv8taGY.js → createLucideIcon-dy5ie7Ox.js} +1 -1
  23. package/dist/assets/desktop-update-config-2BS6BMkW.js +1 -0
  24. package/dist/assets/{dist-DmAlInRu.js → dist-BruyLa92.js} +1 -1
  25. package/dist/assets/{dist-Da5Gm_pO.js → dist-Cy7_j6hA.js} +1 -1
  26. package/dist/assets/download-BD0ETkB-.js +1 -0
  27. package/dist/assets/{external-link-DFjw3x1B.js → external-link-kZSAO8nT.js} +1 -1
  28. package/dist/assets/{hash-DJtaCejM.js → hash-BHJC2Ovu.js} +1 -1
  29. package/dist/assets/i18n-CpTZLchQ.js +1 -0
  30. package/dist/assets/index-mW8W2FUu.css +1 -0
  31. package/dist/assets/index-zDZfXoI4.js +6 -0
  32. package/dist/assets/{infiniteQueryBehavior-DHSEQ3OH.js → infiniteQueryBehavior-CyER9hv0.js} +1 -1
  33. package/dist/assets/loader-circle-Bc2gCU33.js +1 -0
  34. package/dist/assets/{logos-DEFUIR12.js → logos-B7gRObP8.js} +1 -1
  35. package/dist/assets/marketplace-page-3qVMnF3d.js +1 -0
  36. package/dist/assets/marketplace-page-BhFIeQzI.js +49 -0
  37. package/dist/assets/mcp-marketplace-page-DYfteJ1D.js +40 -0
  38. package/dist/assets/{page-layout-Da3i3r6G.js → page-layout-0UcO9H9Z.js} +1 -1
  39. package/dist/assets/play-CKDjSQFL.js +1 -0
  40. package/dist/assets/plus-CG0QrVY_.js +1 -0
  41. package/dist/assets/{refresh-ccw-D6HkNtfz.js → refresh-ccw-COVhNHtN.js} +1 -1
  42. package/dist/assets/{refresh-cw-DRcvRrnc.js → refresh-cw-Bcv40SXy.js} +1 -1
  43. package/dist/assets/remote-access-page-CWHG-sug.js +1 -0
  44. package/dist/assets/{rotate-cw-BmDKfXtH.js → rotate-cw-oHMKJMC8.js} +1 -1
  45. package/dist/assets/{save-DHGmi2e9.js → save-EqJPOF0G.js} +1 -1
  46. package/dist/assets/search-BCAlB8nz.js +1 -0
  47. package/dist/assets/security-config-Slh0Mayz.js +1 -0
  48. package/dist/assets/select-CVz0t7MF.js +41 -0
  49. package/dist/assets/setting-row-CbVHAuQt.js +1 -0
  50. package/dist/assets/skeleton-D5rdKvzy.js +1 -0
  51. package/dist/assets/{status-dot-DurKKSwA.js → status-dot-DpPtVzQT.js} +1 -1
  52. package/dist/assets/{switch-0rmPBRKI.js → switch-CM29eCAR.js} +1 -1
  53. package/dist/assets/{tabs-custom-5JLVL6v8.js → tabs-custom-YcZUWn3o.js} +1 -1
  54. package/dist/assets/tag-chip-DMXdnLcj.js +1 -0
  55. package/dist/assets/{trash-2-C6caKPoz.js → trash-2-mJT6oWa2.js} +1 -1
  56. package/dist/assets/{use-infinite-scroll-loader-dwnaa_qi.js → use-infinite-scroll-loader-DJ1L81Dz.js} +1 -1
  57. package/dist/assets/{useConfirmDialog-mMeWD_yo.js → useConfirmDialog-BsVuqu1x.js} +1 -1
  58. package/dist/assets/{useMutation-BmxxvCNf.js → useMutation-CNcz2fgt.js} +1 -1
  59. package/dist/assets/x-Czwxm82I.js +1 -0
  60. package/dist/index.html +95 -21
  61. package/dist/manifest.webmanifest +30 -0
  62. package/dist/offline.html +102 -0
  63. package/dist/pwa-192.png +0 -0
  64. package/dist/pwa-512.png +0 -0
  65. package/dist/runtime-icons/claude.ico +0 -0
  66. package/dist/runtime-icons/codex-openai.svg +6 -0
  67. package/dist/runtime-icons/hermes-agent.png +0 -0
  68. package/dist/sw.js +80 -0
  69. package/index.html +73 -1
  70. package/package.json +5 -5
  71. package/public/manifest.webmanifest +30 -0
  72. package/public/offline.html +102 -0
  73. package/public/pwa-192.png +0 -0
  74. package/public/pwa-512.png +0 -0
  75. package/public/runtime-icons/claude.ico +0 -0
  76. package/public/runtime-icons/codex-openai.svg +6 -0
  77. package/public/runtime-icons/hermes-agent.png +0 -0
  78. package/public/sw.js +80 -0
  79. package/src/account/components/account-panel.tsx +217 -97
  80. package/src/account/managers/account.manager.ts +3 -2
  81. package/src/api/chat-session-type.types.ts +7 -0
  82. package/src/api/runtime-control.types.ts +8 -0
  83. package/src/api/server-path.ts +27 -4
  84. package/src/api/types.ts +25 -10
  85. package/src/app.tsx +227 -54
  86. package/src/components/agents/agent-dialogs.tsx +499 -0
  87. package/src/components/agents/agents-page.test.tsx +238 -0
  88. package/src/components/agents/agents-page.tsx +435 -0
  89. package/src/components/chat/ChatSidebar.test.tsx +43 -1
  90. package/src/components/chat/ChatSidebar.tsx +35 -35
  91. package/src/components/chat/adapters/chat-message.summary-truncation.test.ts +66 -0
  92. package/src/components/chat/adapters/file-operation/card.ts +9 -0
  93. package/src/components/chat/adapters/file-operation/diff.ts +14 -0
  94. package/src/components/chat/{ChatConversationPanel.test.tsx → chat-conversation-panel.test.tsx} +127 -206
  95. package/src/components/chat/chat-conversation-panel.tsx +482 -0
  96. package/src/components/chat/chat-page-shell.tsx +19 -13
  97. package/src/components/chat/chat-session-type-option-item.test.tsx +46 -0
  98. package/src/components/chat/chat-session-type-option-item.tsx +68 -0
  99. package/src/components/chat/chat-session-workspace-file-preview.test.tsx +178 -0
  100. package/src/components/chat/chat-session-workspace-file-preview.tsx +278 -0
  101. package/src/components/chat/chat-session-workspace-panel-nav.tsx +203 -0
  102. package/src/components/chat/chat-session-workspace-panel.tsx +318 -0
  103. package/src/components/chat/chat-sidebar-project-groups.tsx +11 -36
  104. package/src/components/chat/chat-sidebar-session-item.tsx +32 -2
  105. package/src/components/chat/containers/chat-message-list.container.test.tsx +49 -0
  106. package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
  107. package/src/components/chat/managers/chat-session-list.manager.test.ts +12 -0
  108. package/src/components/chat/managers/chat-session-list.manager.ts +7 -0
  109. package/src/components/chat/ncp/__tests__/ncp-session-adapter.cancelled-tool.test.ts +77 -0
  110. package/src/components/chat/ncp/ncp-chat-page.tsx +9 -7
  111. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +179 -41
  112. package/src/components/chat/ncp/ncp-session-adapter.test.ts +36 -1
  113. package/src/components/chat/ncp/ncp-session-adapter.ts +20 -0
  114. package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +62 -13
  115. package/src/components/chat/ncp/tests/ncp-chat-thread.manager.test.ts +189 -0
  116. package/src/components/chat/presenter/chat-presenter-context.tsx +13 -2
  117. package/src/components/chat/session-header/chat-session-header-actions.test.tsx +26 -0
  118. package/src/components/chat/session-header/chat-session-header-actions.tsx +19 -1
  119. package/src/components/chat/stores/chat-input.store.ts +2 -1
  120. package/src/components/chat/stores/chat-thread.store.ts +27 -1
  121. package/src/components/chat/useChatSessionTypeState.ts +10 -1
  122. package/src/components/chat/workspace/chat-session-workspace-file-breadcrumbs.tsx +86 -0
  123. package/src/components/common/BrandHeader.tsx +3 -1
  124. package/src/components/common/session-context-icon.tsx +15 -2
  125. package/src/components/common/{TagInput.tsx → tag-input.tsx} +25 -17
  126. package/src/components/config/ChannelForm.test.tsx +89 -3
  127. package/src/components/config/ChannelForm.tsx +157 -188
  128. package/src/components/config/ChannelsList.test.tsx +163 -119
  129. package/src/components/config/ChannelsList.tsx +90 -101
  130. package/src/components/config/ProviderForm.tsx +108 -146
  131. package/src/components/config/ProvidersList.tsx +100 -123
  132. package/src/components/config/RuntimeConfig.tsx +141 -2
  133. package/src/components/config/SearchConfig.tsx +423 -393
  134. package/src/components/config/channel-form-fields-section.tsx +70 -37
  135. package/src/components/config/config-split-page.tsx +109 -0
  136. package/src/components/config/provider-enabled-field.tsx +17 -10
  137. package/src/components/config/runtime-control-card.test.tsx +56 -0
  138. package/src/components/config/runtime-control-card.tsx +25 -0
  139. package/src/components/config/runtime-presence-card.tsx +93 -79
  140. package/src/components/layout/AppLayout.tsx +25 -37
  141. package/src/components/layout/app-layout.test.tsx +46 -14
  142. package/src/components/layout/runtime-status-entry.test.tsx +157 -0
  143. package/src/components/layout/runtime-status-entry.tsx +143 -0
  144. package/src/components/marketplace/marketplace-detail-doc.ts +93 -0
  145. package/src/components/marketplace/marketplace-list-card.tsx +288 -0
  146. package/src/components/marketplace/marketplace-page-data.ts +129 -0
  147. package/src/components/marketplace/marketplace-page.test.tsx +339 -0
  148. package/src/components/marketplace/marketplace-page.tsx +596 -0
  149. package/src/components/marketplace/mcp/mcp-marketplace-card.tsx +128 -0
  150. package/src/components/marketplace/mcp/mcp-marketplace-dialogs.tsx +191 -0
  151. package/src/components/marketplace/mcp/mcp-marketplace-doc.ts +152 -0
  152. package/src/components/marketplace/mcp/mcp-marketplace-page.test.tsx +223 -0
  153. package/src/components/marketplace/mcp/mcp-marketplace-page.tsx +414 -0
  154. package/src/components/providers/ThemeProvider.tsx +5 -0
  155. package/src/components/remote/remote-access-page.test.tsx +105 -0
  156. package/src/components/remote/remote-access-page.tsx +248 -0
  157. package/src/components/ui/notice-card.tsx +129 -0
  158. package/src/components/ui/setting-row.tsx +51 -0
  159. package/src/components/ui/tag-chip.tsx +39 -0
  160. package/src/components/ui/textarea.tsx +19 -0
  161. package/src/hooks/server-path/use-server-path-read.ts +20 -0
  162. package/src/hooks/useConfig.ts +2 -1
  163. package/src/index.css +24 -0
  164. package/src/lib/app-resource-uri.test.ts +20 -0
  165. package/src/lib/app-resource-uri.ts +29 -0
  166. package/src/lib/chat-message.ts +14 -3
  167. package/src/lib/i18n.chat.ts +12 -1
  168. package/src/lib/i18n.pwa.ts +62 -0
  169. package/src/lib/i18n.remote.ts +1 -1
  170. package/src/lib/i18n.runtime-control.ts +31 -0
  171. package/src/lib/i18n.ts +7 -10
  172. package/src/lib/session-context.utils.test.ts +71 -0
  173. package/src/lib/session-context.utils.ts +28 -3
  174. package/src/lib/session-project/workspace-file-breadcrumb.test.ts +83 -0
  175. package/src/lib/session-project/workspace-file-breadcrumb.ts +188 -0
  176. package/src/pwa/components/pwa-install-entry.test.tsx +110 -0
  177. package/src/pwa/components/pwa-install-entry.tsx +205 -0
  178. package/src/pwa/managers/pwa-install.manager.test.ts +160 -0
  179. package/src/pwa/managers/pwa-install.manager.ts +232 -0
  180. package/src/pwa/managers/pwa-runtime.manager.ts +196 -0
  181. package/src/pwa/managers/pwa-shell-theme.manager.test.ts +30 -0
  182. package/src/pwa/managers/pwa-shell-theme.manager.ts +46 -0
  183. package/src/pwa/pwa-install-banner.storage.ts +55 -0
  184. package/src/pwa/pwa.types.ts +22 -0
  185. package/src/pwa/register-pwa.ts +14 -0
  186. package/src/pwa/stores/pwa.store.ts +17 -0
  187. package/src/vite-env.d.ts +9 -0
  188. package/dist/assets/ChannelsList-KIQIxluX.js +0 -8
  189. package/dist/assets/DocBrowser-CyDgAtO9.js +0 -1
  190. package/dist/assets/MarketplacePage-BySqkYDh.js +0 -49
  191. package/dist/assets/MarketplacePage-C0olZaek.js +0 -1
  192. package/dist/assets/McpMarketplacePage-DqKaiXO9.js +0 -40
  193. package/dist/assets/ModelConfig-IrmzoslW.js +0 -1
  194. package/dist/assets/ProvidersList-8_Kalfwl.js +0 -1
  195. package/dist/assets/RemoteAccessPage-CyQlSjPf.js +0 -1
  196. package/dist/assets/RuntimeConfig-Bk0uYBhf.js +0 -1
  197. package/dist/assets/SearchConfig-DNBR-UbE.js +0 -1
  198. package/dist/assets/SessionsConfig-Doqp5ghH.js +0 -2
  199. package/dist/assets/chat-page-Bph8M5zo.js +0 -58
  200. package/dist/assets/chat-session-display-CoN3Wmn-.js +0 -1
  201. package/dist/assets/config-layout-DmlGaay2.js +0 -1
  202. package/dist/assets/desktop-update-config-1KBrqLBC.js +0 -1
  203. package/dist/assets/i18n-CwHZ-9vt.js +0 -1
  204. package/dist/assets/index-DafCdM4F.css +0 -1
  205. package/dist/assets/index-DdksE6U3.js +0 -6
  206. package/dist/assets/loader-circle-PsSP0H9n.js +0 -1
  207. package/dist/assets/play-DBQbBxTA.js +0 -1
  208. package/dist/assets/plus-DUOVbsyQ.js +0 -1
  209. package/dist/assets/popover-C_mWOFzI.js +0 -1
  210. package/dist/assets/search-MChQRYR1.js +0 -1
  211. package/dist/assets/security-config-CbXfPZzr.js +0 -1
  212. package/dist/assets/select-Caud8QvU.js +0 -41
  213. package/dist/assets/skeleton-B-4vRq_Z.js +0 -1
  214. package/dist/assets/x-DuMhMATD.js +0 -1
  215. package/src/components/agents/AgentDialogs.tsx +0 -400
  216. package/src/components/agents/AgentsPage.test.tsx +0 -217
  217. package/src/components/agents/AgentsPage.tsx +0 -352
  218. package/src/components/chat/ChatConversationPanel.tsx +0 -256
  219. package/src/components/chat/chat-child-session-panel.tsx +0 -270
  220. package/src/components/config/config-layout.ts +0 -10
  221. package/src/components/marketplace/MarketplacePage.test.tsx +0 -322
  222. package/src/components/marketplace/MarketplacePage.tsx +0 -827
  223. package/src/components/marketplace/mcp/McpMarketplacePage.test.tsx +0 -208
  224. package/src/components/marketplace/mcp/McpMarketplacePage.tsx +0 -580
  225. package/src/components/remote/RemoteAccessPage.test.tsx +0 -103
  226. package/src/components/remote/RemoteAccessPage.tsx +0 -144
  227. /package/dist/assets/{config-hints-BZoDjXye.js → config-hints-BhTmc9P1.js} +0 -0
@@ -1,352 +0,0 @@
1
- import { useMemo, useState } from 'react';
2
- import { useNavigate } from 'react-router-dom';
3
- import { useCreateAgent, useDeleteAgent, useAgents, useUpdateAgent } from '@/hooks/agents/useAgents';
4
- import { useConfig, useConfigMeta } from '@/hooks/useConfig';
5
- import type { AgentProfileView } from '@/api/types';
6
- import {
7
- AgentCreateDialog,
8
- AgentEditDialog,
9
- type AgentCreateFormState,
10
- type AgentEditFormState
11
- } from '@/components/agents/AgentDialogs';
12
- import {
13
- buildSessionTypeOptions,
14
- normalizeSessionType,
15
- resolveAgentRuntimeSessionType,
16
- resolveSessionTypeLabel
17
- } from '@/components/chat/useChatSessionTypeState';
18
- import { useChatInputStore } from '@/components/chat/stores/chat-input.store';
19
- import { useChatSessionListStore } from '@/components/chat/stores/chat-session-list.store';
20
- import { AgentAvatar } from '@/components/common/AgentAvatar';
21
- import { Button } from '@/components/ui/button';
22
- import { Card, CardContent } from '@/components/ui/card';
23
- import { useNcpChatSessionTypes } from '@/hooks/use-ncp-chat-session-types';
24
- import { PageLayout } from '@/components/layout/page-layout';
25
- import { t } from '@/lib/i18n';
26
- import { buildProviderModelCatalog } from '@/lib/provider-models';
27
- import { cn } from '@/lib/utils';
28
- import { Bot, House, MessageCircle, Pencil, Plus, ShieldCheck, Sparkles, Trash2 } from 'lucide-react';
29
-
30
- const CARD_TONES = [
31
- {
32
- strip: 'bg-[#efc37a]',
33
- chip: 'border-[#f2d7a7] bg-[#fff8eb] text-[#8d5a18]'
34
- },
35
- {
36
- strip: 'bg-[#8fd4c0]',
37
- chip: 'border-[#bde6da] bg-[#effbf7] text-[#156653]'
38
- },
39
- {
40
- strip: 'bg-[#b7c9fb]',
41
- chip: 'border-[#d7e2ff] bg-[#f4f7ff] text-[#2d4d8f]'
42
- }
43
- ] as const;
44
-
45
- function resolveAgentTone(index: number, builtIn: boolean) {
46
- if (builtIn) {
47
- return {
48
- strip: 'bg-[#e6b765]',
49
- chip: 'border-[#f2d19c] bg-[#fff8ec] text-[#90550d]'
50
- };
51
- }
52
- return CARD_TONES[index % CARD_TONES.length];
53
- }
54
-
55
- export function AgentsPage() {
56
- const navigate = useNavigate();
57
- const agentsQuery = useAgents();
58
- const configQuery = useConfig();
59
- const configMetaQuery = useConfigMeta();
60
- const sessionTypesQuery = useNcpChatSessionTypes();
61
- const createAgent = useCreateAgent();
62
- const updateAgent = useUpdateAgent();
63
- const deleteAgent = useDeleteAgent();
64
- const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
65
- const [editingAgent, setEditingAgent] = useState<AgentProfileView | null>(null);
66
- const setSessionListSnapshot = useChatSessionListStore((state) => state.setSnapshot);
67
-
68
- const agents = useMemo(() => agentsQuery.data?.agents ?? [], [agentsQuery.data?.agents]);
69
- const sortedAgents = useMemo(
70
- () =>
71
- [...agents].sort(
72
- (left, right) =>
73
- Number(Boolean(right.builtIn)) - Number(Boolean(left.builtIn)) ||
74
- left.id.localeCompare(right.id)
75
- ),
76
- [agents]
77
- );
78
- const providerCatalog = useMemo(
79
- () => buildProviderModelCatalog({ config: configQuery.data, meta: configMetaQuery.data, onlyConfigured: true }),
80
- [configMetaQuery.data, configQuery.data]
81
- );
82
- const runtimeOptions = useMemo(
83
- () => buildSessionTypeOptions(sessionTypesQuery.data?.options ?? []),
84
- [sessionTypesQuery.data?.options]
85
- );
86
- const defaultRuntime = useMemo(
87
- () => normalizeSessionType(sessionTypesQuery.data?.defaultType ?? 'native'),
88
- [sessionTypesQuery.data?.defaultType]
89
- );
90
- const defaultRuntimeLabel = useMemo(
91
- () => runtimeOptions.find((option) => option.value === defaultRuntime)?.label ?? resolveSessionTypeLabel(defaultRuntime),
92
- [defaultRuntime, runtimeOptions]
93
- );
94
-
95
- const handleCreate = async (form: AgentCreateFormState) => {
96
- await createAgent.mutateAsync({
97
- data: {
98
- id: form.id,
99
- ...(form.displayName.trim() ? { displayName: form.displayName.trim() } : {}),
100
- ...(form.description.trim() ? { description: form.description.trim() } : {}),
101
- ...(form.avatar.trim() ? { avatar: form.avatar.trim() } : {}),
102
- ...(form.home.trim() ? { home: form.home.trim() } : {}),
103
- ...(form.model.trim() ? { model: form.model.trim() } : {}),
104
- ...(form.runtime.trim() ? { runtime: form.runtime.trim() } : {})
105
- }
106
- });
107
- setIsCreateDialogOpen(false);
108
- };
109
-
110
- const handleStartEdit = (agent: AgentProfileView) => {
111
- setEditingAgent(agent);
112
- };
113
-
114
- const handleUpdate = async (agentId: string, form: AgentEditFormState) => {
115
- await updateAgent.mutateAsync({
116
- agentId,
117
- data: {
118
- displayName: form.displayName,
119
- description: form.description,
120
- avatar: form.avatar,
121
- model: form.model,
122
- ...(form.runtime.trim() ? { runtime: form.runtime.trim() } : { runtime: "" })
123
- }
124
- });
125
- setEditingAgent(null);
126
- };
127
-
128
- const startChatWithAgent = (agent: AgentProfileView) => {
129
- setSessionListSnapshot({
130
- selectedAgentId: agent.id,
131
- selectedSessionKey: null
132
- });
133
- useChatInputStore.getState().setSnapshot({
134
- pendingSessionType: resolveAgentRuntimeSessionType(agent, defaultRuntime),
135
- pendingProjectRoot: null,
136
- pendingProjectRootSessionKey: null
137
- });
138
- navigate('/chat');
139
- };
140
-
141
- return (
142
- <PageLayout className="space-y-5">
143
- <section className="relative overflow-hidden rounded-[28px] border border-[#f0d6aa] bg-[linear-gradient(135deg,#fff7ea_0%,#fff9f1_32%,#f2fbff_100%)] px-5 py-5 sm:px-6">
144
- <div className="absolute inset-y-0 right-0 w-[46%] bg-[radial-gradient(circle_at_top_right,rgba(255,215,163,0.52),transparent_54%)]" />
145
- <div className="absolute -bottom-10 left-8 h-32 w-32 rounded-full bg-[#ffe6c0]/55 blur-3xl" />
146
- <div className="relative grid gap-5 xl:grid-cols-[minmax(0,1fr)_320px] xl:items-center">
147
- <div className="max-w-3xl space-y-3">
148
- <div className="inline-flex items-center gap-2 rounded-full border border-white/70 bg-white/80 px-3 py-1 text-[11px] font-semibold tracking-[0.16em] text-[#9b6118]">
149
- <Sparkles className="h-3.5 w-3.5" />
150
- {t('agentsHeroEyebrow')}
151
- </div>
152
- <div className="space-y-2">
153
- <h1 className="max-w-2xl text-[30px] font-semibold leading-tight tracking-[-0.05em] text-[#2f2212] sm:text-[38px]">
154
- {t('agentsHeroTitle')}
155
- </h1>
156
- <p className="max-w-2xl text-sm leading-6 text-[#6d5841] sm:text-[15px] sm:leading-7">
157
- {t('agentsHeroDescription')}
158
- </p>
159
- </div>
160
- <div className="pt-1">
161
- <div className="inline-flex items-center gap-3 rounded-2xl border border-[#f2d5a4] bg-white/82 px-3 py-2 text-[#7a4d12] shadow-[0_14px_30px_rgba(167,117,47,0.07)]">
162
- <span className="text-[11px] font-semibold tracking-[0.14em]">
163
- {t('agentsOverviewTotal')}
164
- </span>
165
- <span className="text-xl font-semibold tracking-[-0.04em] text-[#1f2937]">
166
- {agents.length}
167
- </span>
168
- </div>
169
- </div>
170
- </div>
171
- <div className="flex shrink-0 flex-col gap-3">
172
- <Button
173
- type="button"
174
- className="h-10 rounded-2xl bg-[#1f5c4d] px-5 text-sm font-semibold text-white hover:bg-[#184d40]"
175
- onClick={() => setIsCreateDialogOpen(true)}
176
- >
177
- <Plus className="mr-2 h-4 w-4" />
178
- {t('agentsCreateButton')}
179
- </Button>
180
- <div className="rounded-2xl border border-white/70 bg-white/72 px-4 py-3 text-xs leading-6 text-[#6d5841] shadow-[0_18px_40px_rgba(167,117,47,0.08)]">
181
- {t('agentsCreateDialogHint')}
182
- </div>
183
- </div>
184
- </div>
185
- </section>
186
-
187
- <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
188
- {agentsQuery.isLoading ? (
189
- <Card className="md:col-span-2 xl:col-span-3 border-dashed border-[#d9dce3] bg-white/70">
190
- <CardContent className="py-14 text-center text-sm text-gray-500">
191
- {t('agentsLoading')}
192
- </CardContent>
193
- </Card>
194
- ) : sortedAgents.length === 0 ? (
195
- <Card className="md:col-span-2 xl:col-span-3 overflow-hidden border-dashed border-[#d9dce3] bg-[linear-gradient(135deg,#fff7ea_0%,#f4fbff_100%)]">
196
- <CardContent className="flex min-h-[240px] flex-col items-center justify-center px-6 py-14 text-center">
197
- <div className="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-white/80 shadow-[0_18px_44px_rgba(0,0,0,0.08)]">
198
- <Bot className="h-8 w-8 text-[#d39a3b]" />
199
- </div>
200
- <div className="text-lg font-semibold text-[#2f2212]">{t('agentsEmpty')}</div>
201
- <p className="mt-2 max-w-md text-sm leading-6 text-[#78644d]">
202
- {t('agentsEmptyDescription')}
203
- </p>
204
- <Button
205
- type="button"
206
- className="mt-5 rounded-2xl bg-[#1f5c4d] px-5 text-white hover:bg-[#184d40]"
207
- onClick={() => setIsCreateDialogOpen(true)}
208
- >
209
- <Plus className="mr-2 h-4 w-4" />
210
- {t('agentsCreateButton')}
211
- </Button>
212
- </CardContent>
213
- </Card>
214
- ) : (
215
- sortedAgents.map((agent, index) => {
216
- const tone = resolveAgentTone(index, Boolean(agent.builtIn));
217
- const runtimeValue = agent.runtime?.trim() || agent.engine?.trim() || '';
218
- const runtimeLabel = runtimeValue
219
- ? runtimeOptions.find((option) => option.value === normalizeSessionType(runtimeValue))?.label ??
220
- resolveSessionTypeLabel(runtimeValue)
221
- : defaultRuntimeLabel;
222
- return (
223
- <Card
224
- key={agent.id}
225
- className="overflow-hidden border border-gray-200 bg-white shadow-sm transition-shadow duration-200 hover:shadow-md"
226
- >
227
- <div className={cn('h-1.5 w-full', tone.strip)} />
228
- <CardContent className="flex h-full flex-col gap-4 px-4 py-4">
229
- <div className="flex items-start gap-3">
230
- <AgentAvatar
231
- agentId={agent.id}
232
- displayName={agent.displayName}
233
- avatarUrl={agent.avatarUrl}
234
- className="h-11 w-11 shrink-0"
235
- />
236
- <div className="min-w-0 flex-1 space-y-1 pt-0.5">
237
- <div className="flex flex-wrap items-center gap-2">
238
- <div className="truncate text-lg font-semibold tracking-[-0.03em] text-[#1f2937]">
239
- {agent.displayName?.trim() || agent.id}
240
- </div>
241
- {agent.builtIn ? (
242
- <span
243
- className={cn(
244
- 'inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium',
245
- tone.chip
246
- )}
247
- >
248
- <ShieldCheck className="h-3 w-3" />
249
- {t('agentsCardBuiltInTag')}
250
- </span>
251
- ) : null}
252
- </div>
253
- <div className="text-[11px] font-medium uppercase tracking-[0.18em] text-[#94a3b8]">
254
- @{agent.id}
255
- </div>
256
- </div>
257
- </div>
258
-
259
- <p className="text-sm leading-6 text-[#64748b]">
260
- {agent.description?.trim() ||
261
- (agent.builtIn
262
- ? t('agentsCardBuiltInSummary')
263
- : t('agentsCardCustomSummary'))}
264
- </p>
265
-
266
- <div className="mt-auto flex flex-col gap-4">
267
- <div>
268
- <div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#94a3b8]">
269
- <Sparkles className="h-3.5 w-3.5" />
270
- {t('agentsCardRuntimeLabel')}
271
- </div>
272
- <div className="mt-1.5 text-sm leading-6 text-[#475569]">
273
- {runtimeLabel}
274
- </div>
275
- </div>
276
-
277
- <div className="border-t border-gray-100 pt-3">
278
- <div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#94a3b8]">
279
- <House className="h-3.5 w-3.5" />
280
- {t('agentsCardHomeLabel')}
281
- </div>
282
- <div className="mt-1.5 break-all text-sm leading-6 text-[#475569]">
283
- {agent.workspace ?? '-'}
284
- </div>
285
- </div>
286
-
287
- <div className="flex flex-wrap items-center gap-2">
288
- <Button
289
- type="button"
290
- className="h-9 rounded-xl bg-[#1f5c4d] px-4 text-white hover:bg-[#184d40]"
291
- onClick={() => startChatWithAgent(agent)}
292
- >
293
- <MessageCircle className="mr-2 h-4 w-4" />
294
- {t('agentsCardStartChat')}
295
- </Button>
296
- <Button
297
- type="button"
298
- variant="ghost"
299
- className="h-8 rounded-xl px-3 text-xs text-[#7b8794] hover:bg-[#f3f4f6] hover:text-[#475569]"
300
- onClick={() => handleStartEdit(agent)}
301
- disabled={updateAgent.isPending}
302
- >
303
- <Pencil className="mr-1.5 h-3.5 w-3.5" />
304
- {t('agentsEditAction')}
305
- </Button>
306
- {!agent.builtIn ? (
307
- <Button
308
- type="button"
309
- variant="ghost"
310
- className="h-8 rounded-xl px-3 text-xs text-[#7b8794] hover:bg-[#f3f4f6] hover:text-[#475569]"
311
- onClick={() => deleteAgent.mutate({ agentId: agent.id })}
312
- disabled={deleteAgent.isPending}
313
- >
314
- <Trash2 className="mr-1.5 h-3.5 w-3.5" />
315
- {t('agentsRemoveAction')}
316
- </Button>
317
- ) : null}
318
- </div>
319
- </div>
320
- </CardContent>
321
- </Card>
322
- );
323
- })
324
- )}
325
- </div>
326
-
327
- <AgentCreateDialog
328
- open={isCreateDialogOpen}
329
- pending={createAgent.isPending}
330
- providerCatalog={providerCatalog}
331
- runtimeOptions={runtimeOptions}
332
- defaultRuntime={defaultRuntime}
333
- onOpenChange={setIsCreateDialogOpen}
334
- onSubmit={handleCreate}
335
- />
336
-
337
- <AgentEditDialog
338
- agent={editingAgent}
339
- pending={updateAgent.isPending}
340
- providerCatalog={providerCatalog}
341
- runtimeOptions={runtimeOptions}
342
- defaultRuntime={defaultRuntime}
343
- onOpenChange={(open) => {
344
- if (!open && !updateAgent.isPending) {
345
- setEditingAgent(null);
346
- }
347
- }}
348
- onSubmit={handleUpdate}
349
- />
350
- </PageLayout>
351
- );
352
- }
@@ -1,256 +0,0 @@
1
- import { useRef } from "react";
2
- import { ArrowLeft } from "lucide-react";
3
- import { useStickyBottomScroll } from "@nextclaw/agent-chat-ui";
4
- import {
5
- ChatInputBarContainer,
6
- ChatMessageListContainer,
7
- } from "@/components/chat/nextclaw";
8
- import { ChatChildSessionPanel } from "@/components/chat/chat-child-session-panel";
9
- import { ChatWelcome } from "@/components/chat/ChatWelcome";
10
- import { AgentAvatar } from "@/components/common/AgentAvatar";
11
- import { usePresenter } from "@/components/chat/presenter/chat-presenter-context";
12
- import { ChatSessionHeaderActions } from "@/components/chat/session-header/chat-session-header-actions";
13
- import { ChatSessionProjectBadge } from "@/components/chat/session-header/chat-session-project-badge";
14
- import { useChatInputStore } from "@/components/chat/stores/chat-input.store";
15
- import { useChatThreadStore } from "@/components/chat/stores/chat-thread.store";
16
- import { resolveAgentRuntimeSessionType } from "@/components/chat/useChatSessionTypeState";
17
- import { t } from "@/lib/i18n";
18
- import { cn } from "@/lib/utils";
19
-
20
- function ChatConversationSkeleton() {
21
- return (
22
- <section className="flex-1 min-h-0 flex flex-col overflow-hidden bg-gradient-to-b from-gray-50/60 to-white">
23
- <div className="flex-1 min-h-0 overflow-y-auto custom-scrollbar">
24
- <div className="mx-auto w-full max-w-[min(1120px,100%)] px-6 py-5">
25
- <div className="space-y-4">
26
- <div className="h-6 w-48 animate-pulse rounded bg-gray-200" />
27
- <div className="h-24 w-[78%] animate-pulse rounded-2xl bg-gray-200/80" />
28
- <div className="h-20 w-[62%] animate-pulse rounded-2xl bg-gray-200/80" />
29
- <div className="h-28 w-[84%] animate-pulse rounded-2xl bg-gray-200/80" />
30
- </div>
31
- </div>
32
- </div>
33
- <div className="border-t border-gray-200/80 bg-white p-4">
34
- <div className="mx-auto w-full max-w-[min(1120px,100%)]">
35
- <div className="rounded-2xl border border-gray-200 bg-white shadow-card p-4">
36
- <div className="h-16 w-full animate-pulse rounded-xl bg-gray-200/80" />
37
- <div className="mt-3 flex items-center justify-between">
38
- <div className="h-8 w-36 animate-pulse rounded-lg bg-gray-200/80" />
39
- <div className="h-8 w-20 animate-pulse rounded-lg bg-gray-200/80" />
40
- </div>
41
- </div>
42
- </div>
43
- </div>
44
- </section>
45
- );
46
- }
47
-
48
- export function ChatConversationPanel() {
49
- const presenter = usePresenter();
50
- const defaultSessionType = useChatInputStore(
51
- (state) => state.snapshot.defaultSessionType,
52
- );
53
- const snapshot = useChatThreadStore((state) => state.snapshot);
54
- const fallbackThreadRef = useRef<HTMLDivElement | null>(null);
55
- const threadRef = snapshot.threadRef ?? fallbackThreadRef;
56
- const childSessionTabs = snapshot.childSessionTabs.filter(
57
- (tab) => tab.parentSessionKey === snapshot.sessionKey,
58
- );
59
- const detailSessionKey = childSessionTabs.some(
60
- (tab) => tab.sessionKey === snapshot.activeChildSessionKey,
61
- )
62
- ? snapshot.activeChildSessionKey
63
- : (childSessionTabs[childSessionTabs.length - 1]?.sessionKey ?? null);
64
- const shouldShowSessionHeader = Boolean(
65
- snapshot.sessionKey || snapshot.sessionTypeLabel,
66
- );
67
- const sessionHeaderTitle =
68
- snapshot.sessionDisplayName ||
69
- (snapshot.canDeleteSession && snapshot.sessionKey ? snapshot.sessionKey : null) ||
70
- t("chatSidebarNewTask");
71
- const normalizedAgentId = snapshot.agentId?.trim() ?? "";
72
- const shouldShowHeaderAgentAvatar =
73
- normalizedAgentId.length > 0 &&
74
- normalizedAgentId.toLowerCase() !== "main";
75
-
76
- const showWelcome =
77
- !snapshot.canDeleteSession &&
78
- snapshot.messages.length === 0 &&
79
- !snapshot.isSending;
80
- const hasConfiguredModel = snapshot.modelOptions.length > 0;
81
- const shouldShowProviderHint =
82
- snapshot.isProviderStateResolved && !hasConfiguredModel;
83
- const hideEmptyHint =
84
- snapshot.isHistoryLoading &&
85
- snapshot.messages.length === 0 &&
86
- !snapshot.isSending &&
87
- !snapshot.isAwaitingAssistantOutput;
88
- const availableAgents = snapshot.availableAgents ?? [];
89
- const resolveDraftAgent = (agentId: string) =>
90
- availableAgents.find((agent) => agent.id === agentId) ?? null;
91
- const createDraftSessionForAgent = () => {
92
- const sessionType = resolveAgentRuntimeSessionType(
93
- resolveDraftAgent(snapshot.agentId ?? "main"),
94
- defaultSessionType,
95
- );
96
- presenter.chatSessionListManager.createSession(sessionType);
97
- };
98
- const selectDraftAgent = (agentId: string) => {
99
- presenter.chatSessionListManager.setSelectedAgentId(agentId);
100
- presenter.chatInputManager.setPendingSessionType(
101
- resolveAgentRuntimeSessionType(resolveDraftAgent(agentId), defaultSessionType),
102
- );
103
- };
104
-
105
- const { onScroll: handleScroll } = useStickyBottomScroll({
106
- scrollRef: threadRef,
107
- resetKey: snapshot.sessionKey,
108
- isLoading: snapshot.isHistoryLoading,
109
- hasContent: snapshot.messages.length > 0,
110
- contentVersion: snapshot.messages[snapshot.messages.length - 1] ?? null,
111
- });
112
-
113
- if (!snapshot.isProviderStateResolved) {
114
- return <ChatConversationSkeleton />;
115
- }
116
-
117
- return (
118
- <section className="flex-1 min-h-0 flex overflow-hidden bg-gradient-to-b from-gray-50/60 to-white">
119
- <div className="flex min-h-0 flex-1 flex-col overflow-hidden">
120
- {snapshot.parentSessionKey ? (
121
- <div className="border-b border-gray-200/60 bg-white/75 px-5 py-2 backdrop-blur-sm">
122
- <button
123
- type="button"
124
- onClick={presenter.chatThreadManager.goToParentSession}
125
- className="inline-flex items-center gap-2 text-xs font-medium text-gray-600 transition-colors hover:text-gray-900"
126
- >
127
- <ArrowLeft className="h-3.5 w-3.5" />
128
- <span>
129
- {t("chatBackToParent")}
130
- {snapshot.parentSessionLabel?.trim()
131
- ? ` · ${snapshot.parentSessionLabel.trim()}`
132
- : ""}
133
- </span>
134
- </button>
135
- </div>
136
- ) : null}
137
-
138
- <div
139
- className={cn(
140
- "px-5 border-b border-gray-200/60 bg-white/80 backdrop-blur-sm flex items-center justify-between shrink-0 overflow-hidden transition-all duration-200",
141
- shouldShowSessionHeader
142
- ? "py-3 opacity-100"
143
- : "h-0 py-0 opacity-0 border-b-0",
144
- )}
145
- >
146
- <div className="min-w-0 flex-1 flex items-center gap-2">
147
- {shouldShowHeaderAgentAvatar ? (
148
- <div className="inline-flex shrink-0 items-center">
149
- <AgentAvatar
150
- agentId={normalizedAgentId}
151
- displayName={snapshot.agentDisplayName}
152
- avatarUrl={snapshot.agentAvatarUrl}
153
- className="h-5 w-5"
154
- />
155
- </div>
156
- ) : null}
157
- <span className="text-sm font-medium text-gray-700 truncate">
158
- {sessionHeaderTitle}
159
- </span>
160
- {snapshot.sessionTypeLabel ? (
161
- <span className="shrink-0 rounded-full border border-gray-200 bg-gray-100 px-2 py-0.5 text-[11px] font-medium text-gray-600">
162
- {snapshot.sessionTypeLabel}
163
- </span>
164
- ) : null}
165
- {snapshot.sessionProjectName ? (
166
- <ChatSessionProjectBadge
167
- sessionKey={snapshot.sessionKey ?? "draft"}
168
- projectName={snapshot.sessionProjectName}
169
- projectRoot={snapshot.sessionProjectRoot}
170
- persistToServer={snapshot.canDeleteSession}
171
- />
172
- ) : null}
173
- </div>
174
- {snapshot.sessionKey ? (
175
- <ChatSessionHeaderActions
176
- sessionKey={snapshot.sessionKey}
177
- canDeleteSession={snapshot.canDeleteSession}
178
- isDeletePending={snapshot.isDeletePending}
179
- projectRoot={snapshot.sessionProjectRoot}
180
- onDeleteSession={presenter.chatThreadManager.deleteSession}
181
- />
182
- ) : null}
183
- </div>
184
-
185
- {shouldShowProviderHint && (
186
- <div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 flex items-center justify-between gap-3 shrink-0">
187
- <span className="text-xs text-amber-800">
188
- {t("chatModelNoOptions")}
189
- </span>
190
- <button
191
- type="button"
192
- onClick={presenter.chatThreadManager.goToProviders}
193
- className="text-xs font-semibold text-amber-900 underline-offset-2 hover:underline"
194
- >
195
- {t("chatGoConfigureProvider")}
196
- </button>
197
- </div>
198
- )}
199
-
200
- {snapshot.sessionTypeUnavailable &&
201
- snapshot.sessionTypeUnavailableMessage?.trim() && (
202
- <div className="px-5 py-2.5 border-b border-amber-200/70 bg-amber-50/70 shrink-0">
203
- <span className="text-xs text-amber-800">
204
- {snapshot.sessionTypeUnavailableMessage}
205
- </span>
206
- </div>
207
- )}
208
-
209
- <div
210
- ref={threadRef}
211
- onScroll={handleScroll}
212
- className="flex-1 min-h-0 overflow-y-auto custom-scrollbar"
213
- >
214
- {showWelcome ? (
215
- <ChatWelcome
216
- onCreateSession={createDraftSessionForAgent}
217
- agents={availableAgents}
218
- selectedAgentId={snapshot.agentId ?? "main"}
219
- onSelectAgent={selectDraftAgent}
220
- />
221
- ) : hideEmptyHint ? (
222
- <div className="h-full" />
223
- ) : snapshot.messages.length === 0 ? (
224
- <div className="px-5 py-5 text-sm text-gray-500">
225
- {t("chatNoMessages")}
226
- </div>
227
- ) : (
228
- <div className="mx-auto w-full max-w-[min(1120px,100%)] px-6 py-5">
229
- <ChatMessageListContainer
230
- key={snapshot.sessionKey ?? "draft"}
231
- messages={snapshot.messages}
232
- isSending={
233
- snapshot.isSending && snapshot.isAwaitingAssistantOutput
234
- }
235
- onToolAction={presenter.chatThreadManager.openSessionFromToolAction}
236
- />
237
- </div>
238
- )}
239
- </div>
240
-
241
- <ChatInputBarContainer />
242
- </div>
243
-
244
- {detailSessionKey ? (
245
- <ChatChildSessionPanel
246
- tabs={childSessionTabs}
247
- activeSessionKey={detailSessionKey}
248
- onSelectSession={presenter.chatThreadManager.selectChildSessionDetail}
249
- onClose={presenter.chatThreadManager.closeChildSessionDetail}
250
- onBackToParent={presenter.chatThreadManager.goToParentSession}
251
- onToolAction={presenter.chatThreadManager.openSessionFromToolAction}
252
- />
253
- ) : null}
254
- </section>
255
- );
256
- }