@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,19 +1,175 @@
1
- import { Button } from '@/components/ui/button';
2
- import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
3
- import { Input } from '@/components/ui/input';
4
- import { Label } from '@/components/ui/label';
5
- import { useRemoteStatus } from '@/hooks/useRemoteAccess';
6
- import { formatDateTime, t } from '@/lib/i18n';
7
- import { useAccountStore } from '@/account/stores/account.store';
8
- import { useAppPresenter } from '@/presenter/app-presenter-context';
9
- import { KeyRound, LogOut, SquareArrowOutUpRight } from 'lucide-react';
10
- import { useEffect, useState } from 'react';
1
+ import { Button } from "@/components/ui/button";
2
+ import {
3
+ Dialog,
4
+ DialogContent,
5
+ DialogDescription,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ } from "@/components/ui/dialog";
9
+ import { Input } from "@/components/ui/input";
10
+ import { Label } from "@/components/ui/label";
11
+ import { NoticeCard } from "@/components/ui/notice-card";
12
+ import { useRemoteStatus } from "@/hooks/useRemoteAccess";
13
+ import { formatDateTime, t } from "@/lib/i18n";
14
+ import { useAccountStore } from "@/account/stores/account.store";
15
+ import { useAppPresenter } from "@/presenter/app-presenter-context";
16
+ import { KeyRound, LogOut, SquareArrowOutUpRight } from "lucide-react";
17
+ import { useEffect, useState } from "react";
11
18
 
12
- function AccountValueRow({ label, value }: { label: string; value?: string | null }) {
19
+ function AccountValueRow({
20
+ label,
21
+ value,
22
+ }: {
23
+ label: string;
24
+ value?: string | null;
25
+ }) {
13
26
  return (
14
27
  <div className="flex items-start justify-between gap-4 py-2 text-sm">
15
28
  <span className="text-gray-500">{label}</span>
16
- <span className="text-right text-gray-900">{value?.trim() || '-'}</span>
29
+ <span className="text-right text-gray-900">{value?.trim() || "-"}</span>
30
+ </div>
31
+ );
32
+ }
33
+
34
+ function SignedInAccountSection(props: {
35
+ email?: string | null;
36
+ username?: string | null;
37
+ role?: string | null;
38
+ canSubmitUsername: boolean;
39
+ savingUsername: boolean;
40
+ usernameDraft: string;
41
+ onUsernameDraftChange: (value: string) => void;
42
+ onSubmitUsername: () => Promise<void>;
43
+ onOpenDeviceList: () => Promise<void>;
44
+ onLogout: () => Promise<void>;
45
+ }) {
46
+ const {
47
+ email,
48
+ username,
49
+ role,
50
+ canSubmitUsername,
51
+ savingUsername,
52
+ usernameDraft,
53
+ onUsernameDraftChange,
54
+ onSubmitUsername,
55
+ onOpenDeviceList,
56
+ onLogout,
57
+ } = props;
58
+
59
+ return (
60
+ <div className="space-y-4">
61
+ <NoticeCard
62
+ tone="success"
63
+ title={t("accountPanelSignedInTitle")}
64
+ description={t("accountPanelSignedInDescription")}
65
+ />
66
+ <NoticeCard tone="neutral">
67
+ <AccountValueRow label={t("remoteAccountEmail")} value={email} />
68
+ <AccountValueRow label={t("remoteAccountUsername")} value={username} />
69
+ <AccountValueRow label={t("remoteAccountRole")} value={role} />
70
+ </NoticeCard>
71
+ {username ? (
72
+ <p className="text-xs text-gray-500">
73
+ {t("remoteAccountUsernameLockedHelp")}
74
+ </p>
75
+ ) : (
76
+ <NoticeCard
77
+ tone="warning"
78
+ title={t("remoteAccountUsernameRequiredTitle")}
79
+ description={t("remoteAccountUsernameRequiredDescription")}
80
+ >
81
+ <div className="mt-4 space-y-2">
82
+ <Label htmlFor="account-panel-username">
83
+ {t("remoteAccountUsername")}
84
+ </Label>
85
+ <Input
86
+ id="account-panel-username"
87
+ value={usernameDraft}
88
+ onChange={(event) => onUsernameDraftChange(event.target.value)}
89
+ placeholder={t("remoteAccountUsernamePlaceholder")}
90
+ autoCapitalize="none"
91
+ autoCorrect="off"
92
+ spellCheck={false}
93
+ />
94
+ </div>
95
+ <div className="mt-4 flex flex-wrap gap-3">
96
+ <Button
97
+ disabled={!canSubmitUsername}
98
+ onClick={() => void onSubmitUsername()}
99
+ >
100
+ {savingUsername
101
+ ? t("remoteAccountUsernameSaving")
102
+ : t("remoteAccountUsernameSave")}
103
+ </Button>
104
+ </div>
105
+ </NoticeCard>
106
+ )}
107
+ <div className="flex flex-wrap gap-3">
108
+ <Button onClick={() => void onOpenDeviceList()}>
109
+ <SquareArrowOutUpRight className="mr-2 h-4 w-4" />
110
+ {t("remoteOpenDeviceList")}
111
+ </Button>
112
+ <Button variant="outline" onClick={() => void onLogout()}>
113
+ <LogOut className="mr-2 h-4 w-4" />
114
+ {t("remoteLogout")}
115
+ </Button>
116
+ </div>
117
+ </div>
118
+ );
119
+ }
120
+
121
+ function SignedOutAccountSection(props: {
122
+ authSessionId?: string | null;
123
+ authExpiresAt?: string | null;
124
+ authStatusMessage?: string | null;
125
+ authVerificationUri?: string | null;
126
+ onStartBrowserSignIn: () => Promise<void>;
127
+ onResumeBrowserSignIn: () => void;
128
+ }) {
129
+ const {
130
+ authSessionId,
131
+ authExpiresAt,
132
+ authStatusMessage,
133
+ authVerificationUri,
134
+ onStartBrowserSignIn,
135
+ onResumeBrowserSignIn,
136
+ } = props;
137
+
138
+ return (
139
+ <div className="space-y-4">
140
+ <NoticeCard
141
+ tone="neutral"
142
+ title={t("accountPanelSignedOutTitle")}
143
+ description={t("accountPanelSignedOutDescription")}
144
+ >
145
+ {authSessionId ? (
146
+ <div className="mt-3 border-t border-white/80 pt-3">
147
+ <AccountValueRow
148
+ label={t("remoteBrowserAuthSession")}
149
+ value={authSessionId}
150
+ />
151
+ <AccountValueRow
152
+ label={t("remoteBrowserAuthExpiresAt")}
153
+ value={authExpiresAt ? formatDateTime(authExpiresAt) : "-"}
154
+ />
155
+ </div>
156
+ ) : null}
157
+ </NoticeCard>
158
+ {authStatusMessage ? (
159
+ <p className="text-sm text-gray-600">{authStatusMessage}</p>
160
+ ) : null}
161
+ <div className="flex flex-wrap gap-3">
162
+ <Button onClick={() => void onStartBrowserSignIn()}>
163
+ {authSessionId
164
+ ? t("remoteBrowserAuthActionRetry")
165
+ : t("remoteBrowserAuthAction")}
166
+ </Button>
167
+ {authVerificationUri ? (
168
+ <Button variant="outline" onClick={onResumeBrowserSignIn}>
169
+ {t("remoteBrowserAuthResume")}
170
+ </Button>
171
+ ) : null}
172
+ </div>
17
173
  </div>
18
174
  );
19
175
  }
@@ -23,111 +179,75 @@ export function AccountPanel() {
23
179
  const remoteStatus = useRemoteStatus();
24
180
  const panelOpen = useAccountStore((state) => state.panelOpen);
25
181
  const authSessionId = useAccountStore((state) => state.authSessionId);
26
- const authVerificationUri = useAccountStore((state) => state.authVerificationUri);
182
+ const authVerificationUri = useAccountStore(
183
+ (state) => state.authVerificationUri,
184
+ );
27
185
  const authExpiresAt = useAccountStore((state) => state.authExpiresAt);
28
186
  const authStatusMessage = useAccountStore((state) => state.authStatusMessage);
29
187
  const status = remoteStatus.data;
30
- const [usernameDraft, setUsernameDraft] = useState('');
188
+ const [usernameDraft, setUsernameDraft] = useState("");
31
189
  const [savingUsername, setSavingUsername] = useState(false);
32
190
 
33
191
  useEffect(() => {
34
192
  presenter.accountManager.syncRemoteStatus(status);
35
193
  }, [presenter, status]);
36
194
 
37
- const canSubmitUsername = !savingUsername && usernameDraft.trim().length > 0 && !status?.account.username;
195
+ const canSubmitUsername =
196
+ !savingUsername &&
197
+ usernameDraft.trim().length > 0 &&
198
+ !status?.account.username;
38
199
 
39
200
  return (
40
- <Dialog open={panelOpen} onOpenChange={(open) => (open ? presenter.accountManager.openAccountPanel() : presenter.accountManager.closeAccountPanel())}>
201
+ <Dialog
202
+ open={panelOpen}
203
+ onOpenChange={(open) =>
204
+ open
205
+ ? presenter.accountManager.openAccountPanel()
206
+ : presenter.accountManager.closeAccountPanel()
207
+ }
208
+ >
41
209
  <DialogContent className="max-w-xl">
42
210
  <DialogHeader>
43
211
  <DialogTitle className="flex items-center gap-2">
44
212
  <KeyRound className="h-5 w-5 text-primary" />
45
- {t('accountPanelTitle')}
213
+ {t("accountPanelTitle")}
46
214
  </DialogTitle>
47
- <DialogDescription>{t('accountPanelDescription')}</DialogDescription>
215
+ <DialogDescription>{t("accountPanelDescription")}</DialogDescription>
48
216
  </DialogHeader>
49
217
 
50
218
  {status?.account.loggedIn ? (
51
- <div className="space-y-4">
52
- <div className="rounded-2xl border border-emerald-200 bg-emerald-50 px-4 py-3">
53
- <p className="text-sm font-medium text-emerald-800">{t('accountPanelSignedInTitle')}</p>
54
- <p className="mt-1 text-sm text-emerald-700">{t('accountPanelSignedInDescription')}</p>
55
- </div>
56
- <div className="rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3">
57
- <AccountValueRow label={t('remoteAccountEmail')} value={status.account.email} />
58
- <AccountValueRow label={t('remoteAccountUsername')} value={status.account.username} />
59
- <AccountValueRow label={t('remoteAccountRole')} value={status.account.role} />
60
- </div>
61
- {status.account.username ? (
62
- <p className="text-xs text-gray-500">{t('remoteAccountUsernameLockedHelp')}</p>
63
- ) : (
64
- <div className="rounded-2xl border border-amber-200 bg-amber-50 px-4 py-4">
65
- <p className="text-sm font-medium text-amber-900">{t('remoteAccountUsernameRequiredTitle')}</p>
66
- <p className="mt-1 text-sm text-amber-800">{t('remoteAccountUsernameRequiredDescription')}</p>
67
- <div className="mt-4 space-y-2">
68
- <Label htmlFor="account-panel-username">{t('remoteAccountUsername')}</Label>
69
- <Input
70
- id="account-panel-username"
71
- value={usernameDraft}
72
- onChange={(event) => setUsernameDraft(event.target.value)}
73
- placeholder={t('remoteAccountUsernamePlaceholder')}
74
- autoCapitalize="none"
75
- autoCorrect="off"
76
- spellCheck={false}
77
- />
78
- </div>
79
- <div className="mt-4 flex flex-wrap gap-3">
80
- <Button
81
- disabled={!canSubmitUsername}
82
- onClick={async () => {
83
- setSavingUsername(true);
84
- try {
85
- await presenter.accountManager.updateUsername(usernameDraft);
86
- } finally {
87
- setSavingUsername(false);
88
- }
89
- }}
90
- >
91
- {savingUsername ? t('remoteAccountUsernameSaving') : t('remoteAccountUsernameSave')}
92
- </Button>
93
- </div>
94
- </div>
95
- )}
96
- <div className="flex flex-wrap gap-3">
97
- <Button onClick={() => void presenter.accountManager.openNextClawWeb()}>
98
- <SquareArrowOutUpRight className="mr-2 h-4 w-4" />
99
- {t('remoteOpenDeviceList')}
100
- </Button>
101
- <Button variant="outline" onClick={() => void presenter.accountManager.logout()}>
102
- <LogOut className="mr-2 h-4 w-4" />
103
- {t('remoteLogout')}
104
- </Button>
105
- </div>
106
- </div>
219
+ <SignedInAccountSection
220
+ email={status.account.email}
221
+ username={status.account.username}
222
+ role={status.account.role}
223
+ canSubmitUsername={canSubmitUsername}
224
+ savingUsername={savingUsername}
225
+ usernameDraft={usernameDraft}
226
+ onUsernameDraftChange={setUsernameDraft}
227
+ onSubmitUsername={async () => {
228
+ setSavingUsername(true);
229
+ try {
230
+ await presenter.accountManager.updateUsername(usernameDraft);
231
+ } finally {
232
+ setSavingUsername(false);
233
+ }
234
+ }}
235
+ onOpenDeviceList={() => presenter.accountManager.openNextClawWeb('/account')}
236
+ onLogout={() => presenter.accountManager.logout()}
237
+ />
107
238
  ) : (
108
- <div className="space-y-4">
109
- <div className="rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3">
110
- <p className="text-sm font-medium text-gray-900">{t('accountPanelSignedOutTitle')}</p>
111
- <p className="mt-1 text-sm text-gray-600">{t('accountPanelSignedOutDescription')}</p>
112
- {authSessionId ? (
113
- <div className="mt-3 border-t border-white/80 pt-3">
114
- <AccountValueRow label={t('remoteBrowserAuthSession')} value={authSessionId} />
115
- <AccountValueRow label={t('remoteBrowserAuthExpiresAt')} value={authExpiresAt ? formatDateTime(authExpiresAt) : '-'} />
116
- </div>
117
- ) : null}
118
- </div>
119
- {authStatusMessage ? <p className="text-sm text-gray-600">{authStatusMessage}</p> : null}
120
- <div className="flex flex-wrap gap-3">
121
- <Button onClick={() => void presenter.accountManager.startBrowserSignIn()}>
122
- {authSessionId ? t('remoteBrowserAuthActionRetry') : t('remoteBrowserAuthAction')}
123
- </Button>
124
- {authVerificationUri ? (
125
- <Button variant="outline" onClick={() => presenter.accountManager.resumeBrowserSignIn()}>
126
- {t('remoteBrowserAuthResume')}
127
- </Button>
128
- ) : null}
129
- </div>
130
- </div>
239
+ <SignedOutAccountSection
240
+ authSessionId={authSessionId}
241
+ authExpiresAt={authExpiresAt}
242
+ authStatusMessage={authStatusMessage}
243
+ authVerificationUri={authVerificationUri}
244
+ onStartBrowserSignIn={() =>
245
+ presenter.accountManager.startBrowserSignIn()
246
+ }
247
+ onResumeBrowserSignIn={() =>
248
+ presenter.accountManager.resumeBrowserSignIn()
249
+ }
250
+ />
131
251
  )}
132
252
  </DialogContent>
133
253
  </Dialog>
@@ -118,14 +118,15 @@ export class AccountManager {
118
118
  }
119
119
  };
120
120
 
121
- openNextClawWeb = async () => {
121
+ openNextClawWeb = async (path = '/') => {
122
122
  const status = await ensureRemoteStatus();
123
123
  const webBase = resolveRemoteWebBase(status);
124
124
  if (!webBase) {
125
125
  toast.error(t('remoteOpenWebUnavailable'));
126
126
  return;
127
127
  }
128
- window.open(webBase, '_blank', 'noopener,noreferrer');
128
+ const targetUrl = new URL(path, `${webBase.replace(/\/+$/, '')}/`).toString();
129
+ window.open(targetUrl, '_blank', 'noopener,noreferrer');
129
130
  };
130
131
 
131
132
  private scheduleBrowserAuthPoll = () => {
@@ -1,3 +1,9 @@
1
+ export type ChatSessionTypeIconView = {
2
+ kind: "image";
3
+ src: string;
4
+ alt?: string | null;
5
+ };
6
+
1
7
  export type ChatSessionTypeCtaView = {
2
8
  kind: string;
3
9
  label?: string;
@@ -7,6 +13,7 @@ export type ChatSessionTypeCtaView = {
7
13
  export type ChatSessionTypeOptionView = {
8
14
  value: string;
9
15
  label: string;
16
+ icon?: ChatSessionTypeIconView | null;
10
17
  ready?: boolean;
11
18
  reason?: string | null;
12
19
  reasonMessage?: string | null;
@@ -31,6 +31,13 @@ export type RuntimeServiceState =
31
31
  | 'restarting'
32
32
  | 'unknown';
33
33
 
34
+ export type RuntimePendingRestart = {
35
+ changedPaths: string[];
36
+ message: string;
37
+ reasons: string[];
38
+ requestedAt: string;
39
+ };
40
+
34
41
  export type RuntimeControlView = {
35
42
  environment: RuntimeControlEnvironment;
36
43
  lifecycle: RuntimeLifecycleState;
@@ -39,6 +46,7 @@ export type RuntimeControlView = {
39
46
  canRestartService: RuntimeActionCapability;
40
47
  canStopService: RuntimeActionCapability;
41
48
  canRestartApp: RuntimeActionCapability;
49
+ pendingRestart?: RuntimePendingRestart | null;
42
50
  ownerLabel?: string;
43
51
  managementHint?: string;
44
52
  message?: string;
@@ -1,15 +1,17 @@
1
1
  import { api } from './client';
2
- import type { ServerPathBrowseView } from './types';
2
+ import type { ServerPathBrowseView, ServerPathReadView } from './types';
3
3
 
4
4
  export async function fetchServerPathBrowse(params?: {
5
5
  path?: string | null;
6
6
  includeFiles?: boolean;
7
7
  }): Promise<ServerPathBrowseView> {
8
+ const path = typeof params?.path === 'string' ? params.path.trim() : '';
9
+ const includeFiles = Boolean(params?.includeFiles);
8
10
  const query = new URLSearchParams();
9
- if (typeof params?.path === 'string' && params.path.trim().length > 0) {
10
- query.set('path', params.path.trim());
11
+ if (path) {
12
+ query.set('path', path);
11
13
  }
12
- if (params?.includeFiles) {
14
+ if (includeFiles) {
13
15
  query.set('includeFiles', '1');
14
16
  }
15
17
  const suffix = query.toString();
@@ -21,3 +23,24 @@ export async function fetchServerPathBrowse(params?: {
21
23
  }
22
24
  return response.data;
23
25
  }
26
+
27
+ export async function fetchServerPathRead(params: {
28
+ path: string;
29
+ basePath?: string | null;
30
+ }): Promise<ServerPathReadView> {
31
+ const { path } = params;
32
+ const basePath =
33
+ typeof params.basePath === 'string' ? params.basePath.trim() : '';
34
+ const query = new URLSearchParams();
35
+ query.set('path', path.trim());
36
+ if (basePath) {
37
+ query.set('basePath', basePath);
38
+ }
39
+ const response = await api.get<ServerPathReadView>(
40
+ `/api/server-paths/read?${query.toString()}`
41
+ );
42
+ if (!response.ok) {
43
+ throw new Error(response.error.message);
44
+ }
45
+ return response.data;
46
+ }
package/src/api/types.ts CHANGED
@@ -246,6 +246,20 @@ export type SessionConfigView = {
246
246
  dmScope?: "main" | "per-peer" | "per-channel-peer" | "per-account-channel-peer";
247
247
  };
248
248
 
249
+ export type SessionTypeIconView = {
250
+ kind: "image";
251
+ src: string;
252
+ alt?: string | null;
253
+ };
254
+
255
+ export type RuntimeEntryView = {
256
+ enabled?: boolean;
257
+ label?: string;
258
+ icon?: SessionTypeIconView | null;
259
+ type: string;
260
+ config?: Record<string, unknown>;
261
+ };
262
+
249
263
  export type SessionEntryView = {
250
264
  key: string;
251
265
  createdAt: string;
@@ -347,17 +361,9 @@ export type SessionPatchUpdate = {
347
361
  clearHistory?: boolean;
348
362
  };
349
363
 
350
- export type ServerPathEntryView = {
351
- name: string;
352
- path: string;
353
- kind: "directory" | "file";
354
- hidden: boolean;
355
- };
364
+ export type ServerPathEntryView = { name: string; path: string; kind: "directory" | "file"; hidden: boolean };
356
365
 
357
- export type ServerPathBreadcrumbView = {
358
- label: string;
359
- path: string;
360
- };
366
+ export type ServerPathBreadcrumbView = { label: string; path: string };
361
367
 
362
368
  export type ServerPathBrowseView = {
363
369
  currentPath: string;
@@ -367,6 +373,8 @@ export type ServerPathBrowseView = {
367
373
  entries: ServerPathEntryView[];
368
374
  };
369
375
 
376
+ export type ServerPathReadView = { requestedPath: string; resolvedPath: string; kind: "text" | "markdown" | "binary"; sizeBytes: number; truncated: boolean; text?: string; languageHint?: string | null };
377
+
370
378
  export type {
371
379
  ChatSessionTypeCtaView,
372
380
  ChatSessionTypeOptionView,
@@ -431,6 +439,9 @@ export type RuntimeConfigUpdate = {
431
439
  engine?: string;
432
440
  engineConfig?: Record<string, unknown>;
433
441
  };
442
+ runtimes?: {
443
+ entries?: Record<string, RuntimeEntryView> | null;
444
+ };
434
445
  list?: AgentProfileView[];
435
446
  };
436
447
  bindings?: AgentBindingView[];
@@ -500,6 +511,9 @@ export type ConfigView = {
500
511
  contextTokens?: number;
501
512
  maxToolIterations?: number;
502
513
  };
514
+ runtimes?: {
515
+ entries?: Record<string, RuntimeEntryView>;
516
+ };
503
517
  list?: AgentProfileView[];
504
518
  context?: {
505
519
  bootstrap?: {
@@ -670,6 +684,7 @@ export type ConfigActionExecuteResult = {
670
684
  // WebSocket events
671
685
  export type WsEvent =
672
686
  | { type: 'config.updated'; payload: { path: string } }
687
+ | { type: 'channel.config.apply-status'; payload: { channel: string; status: 'started' | 'succeeded' | 'failed'; message?: string } }
673
688
  | { type: 'session.updated'; payload: { sessionKey: string } }
674
689
  | { type: 'session.run-status'; payload: { sessionKey: string; status: 'running' | 'idle' } }
675
690
  | { type: 'session.summary.upsert'; payload: { summary: NcpSessionSummaryView } }