@nextclaw/ui 0.12.9 → 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 (178) hide show
  1. package/CHANGELOG.md +61 -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-6ReNjvzF.js → DocBrowser-DMfr0Oow.js} +1 -1
  5. package/dist/assets/{DocBrowserContext-B6SpA7Qs.js → DocBrowserContext-BXydqby-.js} +1 -1
  6. package/dist/assets/{LogoBadge-ByNLYg65.js → LogoBadge-hO7tY7hE.js} +1 -1
  7. package/dist/assets/ModelConfig-CNIgLf0e.js +1 -0
  8. package/dist/assets/{ProviderScopedModelInput-Da7khnBA.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-D281Rotl.js → SecretsConfig-MEt6MjuD.js} +2 -2
  13. package/dist/assets/SessionsConfig-DifCiXwR.js +2 -0
  14. package/dist/assets/{app-query-client-VnFElj4E.js → app-query-client-9jNewezV.js} +1 -1
  15. package/dist/assets/{book-open-BdcxxoQu.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-DK5HPmIK.js → chunk-JZWAC4HX-C5dEc8hV.js} +1 -1
  19. package/dist/assets/{client-_i4MU2bB.js → client-C-8fH7-c.js} +1 -1
  20. package/dist/assets/{config-DtIQwrHF.js → config-CBScxsdV.js} +1 -1
  21. package/dist/assets/config-split-page-BUout_Ak.js +1 -0
  22. package/dist/assets/{createLucideIcon-BSeTgkZW.js → createLucideIcon-dy5ie7Ox.js} +1 -1
  23. package/dist/assets/desktop-update-config-2BS6BMkW.js +1 -0
  24. package/dist/assets/{dist-ccBFUi-o.js → dist-BruyLa92.js} +1 -1
  25. package/dist/assets/{dist-6TrrnPCR.js → dist-Cy7_j6hA.js} +1 -1
  26. package/dist/assets/{download-BhDxnyvU.js → download-BD0ETkB-.js} +1 -1
  27. package/dist/assets/{external-link-BgErLCNT.js → external-link-kZSAO8nT.js} +1 -1
  28. package/dist/assets/{hash-Bl7dr_UG.js → hash-BHJC2Ovu.js} +1 -1
  29. package/dist/assets/{i18n-eDHeDY0n.js → i18n-CpTZLchQ.js} +1 -1
  30. package/dist/assets/index-mW8W2FUu.css +1 -0
  31. package/dist/assets/index-zDZfXoI4.js +6 -0
  32. package/dist/assets/{infiniteQueryBehavior-ZDS92Qpp.js → infiniteQueryBehavior-CyER9hv0.js} +1 -1
  33. package/dist/assets/loader-circle-Bc2gCU33.js +1 -0
  34. package/dist/assets/{logos-x89HbrZ4.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-vZnghcFy.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-DT98i__E.js → refresh-ccw-COVhNHtN.js} +1 -1
  42. package/dist/assets/{refresh-cw-C47QSEwg.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-JtFzpNn6.js → rotate-cw-oHMKJMC8.js} +1 -1
  45. package/dist/assets/{save-3S6-H3Xw.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-vbanNPFU.js → status-dot-DpPtVzQT.js} +1 -1
  52. package/dist/assets/{switch-BsLtHOH-.js → switch-CM29eCAR.js} +1 -1
  53. package/dist/assets/{tabs-custom-D3HYMt6k.js → tabs-custom-YcZUWn3o.js} +1 -1
  54. package/dist/assets/tag-chip-DMXdnLcj.js +1 -0
  55. package/dist/assets/{trash-2-G48scll7.js → trash-2-mJT6oWa2.js} +1 -1
  56. package/dist/assets/{use-infinite-scroll-loader-DkNhD-42.js → use-infinite-scroll-loader-DJ1L81Dz.js} +1 -1
  57. package/dist/assets/{useConfirmDialog-BkvTN-vd.js → useConfirmDialog-BsVuqu1x.js} +1 -1
  58. package/dist/assets/{useMutation-CBWjE2uj.js → useMutation-CNcz2fgt.js} +1 -1
  59. package/dist/assets/x-Czwxm82I.js +1 -0
  60. package/dist/index.html +22 -22
  61. package/dist/runtime-icons/claude.ico +0 -0
  62. package/dist/runtime-icons/codex-openai.svg +6 -0
  63. package/dist/runtime-icons/hermes-agent.png +0 -0
  64. package/package.json +6 -6
  65. package/public/runtime-icons/claude.ico +0 -0
  66. package/public/runtime-icons/codex-openai.svg +6 -0
  67. package/public/runtime-icons/hermes-agent.png +0 -0
  68. package/src/account/components/account-panel.tsx +217 -97
  69. package/src/account/managers/account.manager.ts +3 -2
  70. package/src/api/chat-session-type.types.ts +7 -0
  71. package/src/api/runtime-control.types.ts +8 -0
  72. package/src/api/types.ts +8 -0
  73. package/src/app.tsx +221 -57
  74. package/src/components/agents/agent-dialogs.tsx +499 -0
  75. package/src/components/agents/agents-page.test.tsx +238 -0
  76. package/src/components/agents/agents-page.tsx +435 -0
  77. package/src/components/chat/ChatSidebar.tsx +11 -35
  78. package/src/components/chat/chat-conversation-panel.test.tsx +20 -0
  79. package/src/components/chat/chat-conversation-panel.tsx +83 -13
  80. package/src/components/chat/chat-page-shell.tsx +19 -13
  81. package/src/components/chat/chat-session-type-option-item.test.tsx +46 -0
  82. package/src/components/chat/chat-session-type-option-item.tsx +68 -0
  83. package/src/components/chat/chat-session-workspace-file-preview.test.tsx +87 -0
  84. package/src/components/chat/chat-session-workspace-file-preview.tsx +14 -43
  85. package/src/components/chat/chat-session-workspace-panel-nav.tsx +8 -2
  86. package/src/components/chat/chat-sidebar-project-groups.tsx +11 -36
  87. package/src/components/chat/ncp/__tests__/ncp-session-adapter.cancelled-tool.test.ts +77 -0
  88. package/src/components/chat/ncp/ncp-chat-page.tsx +2 -0
  89. package/src/components/chat/ncp/ncp-session-adapter.test.ts +1 -0
  90. package/src/components/chat/ncp/ncp-session-adapter.ts +3 -0
  91. package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +10 -4
  92. package/src/components/chat/stores/chat-input.store.ts +2 -1
  93. package/src/components/chat/stores/chat-thread.store.ts +3 -1
  94. package/src/components/chat/useChatSessionTypeState.ts +10 -1
  95. package/src/components/chat/workspace/chat-session-workspace-file-breadcrumbs.tsx +86 -0
  96. package/src/components/common/BrandHeader.tsx +3 -1
  97. package/src/components/common/session-context-icon.tsx +15 -2
  98. package/src/components/common/{TagInput.tsx → tag-input.tsx} +25 -17
  99. package/src/components/config/ChannelForm.test.tsx +89 -3
  100. package/src/components/config/ChannelForm.tsx +157 -188
  101. package/src/components/config/ChannelsList.test.tsx +163 -119
  102. package/src/components/config/ChannelsList.tsx +90 -101
  103. package/src/components/config/ProviderForm.tsx +108 -146
  104. package/src/components/config/ProvidersList.tsx +100 -123
  105. package/src/components/config/SearchConfig.tsx +423 -393
  106. package/src/components/config/channel-form-fields-section.tsx +70 -37
  107. package/src/components/config/config-split-page.tsx +109 -0
  108. package/src/components/config/provider-enabled-field.tsx +17 -10
  109. package/src/components/config/runtime-control-card.test.tsx +56 -0
  110. package/src/components/config/runtime-control-card.tsx +25 -0
  111. package/src/components/config/runtime-presence-card.tsx +93 -79
  112. package/src/components/layout/AppLayout.tsx +25 -37
  113. package/src/components/layout/app-layout.test.tsx +46 -14
  114. package/src/components/layout/runtime-status-entry.test.tsx +157 -0
  115. package/src/components/layout/runtime-status-entry.tsx +143 -0
  116. package/src/components/marketplace/marketplace-detail-doc.ts +93 -0
  117. package/src/components/marketplace/marketplace-list-card.tsx +288 -0
  118. package/src/components/marketplace/marketplace-page-data.ts +129 -0
  119. package/src/components/marketplace/marketplace-page.test.tsx +339 -0
  120. package/src/components/marketplace/marketplace-page.tsx +596 -0
  121. package/src/components/marketplace/mcp/mcp-marketplace-card.tsx +128 -0
  122. package/src/components/marketplace/mcp/mcp-marketplace-dialogs.tsx +191 -0
  123. package/src/components/marketplace/mcp/mcp-marketplace-doc.ts +152 -0
  124. package/src/components/marketplace/mcp/mcp-marketplace-page.test.tsx +223 -0
  125. package/src/components/marketplace/mcp/mcp-marketplace-page.tsx +414 -0
  126. package/src/components/remote/remote-access-page.test.tsx +105 -0
  127. package/src/components/remote/remote-access-page.tsx +248 -0
  128. package/src/components/ui/notice-card.tsx +129 -0
  129. package/src/components/ui/setting-row.tsx +51 -0
  130. package/src/components/ui/tag-chip.tsx +39 -0
  131. package/src/components/ui/textarea.tsx +19 -0
  132. package/src/hooks/useConfig.ts +2 -1
  133. package/src/index.css +24 -0
  134. package/src/lib/app-resource-uri.test.ts +20 -0
  135. package/src/lib/app-resource-uri.ts +29 -0
  136. package/src/lib/i18n.remote.ts +1 -1
  137. package/src/lib/i18n.runtime-control.ts +31 -0
  138. package/src/lib/i18n.ts +5 -8
  139. package/src/lib/session-context.utils.test.ts +71 -0
  140. package/src/lib/session-context.utils.ts +28 -3
  141. package/src/lib/session-project/workspace-file-breadcrumb.test.ts +83 -0
  142. package/src/lib/session-project/workspace-file-breadcrumb.ts +188 -0
  143. package/dist/assets/ChannelsList-Ita2Zm1_.js +0 -8
  144. package/dist/assets/DocBrowser-BNwbPHf4.js +0 -1
  145. package/dist/assets/MarketplacePage-CjX2MWww.js +0 -1
  146. package/dist/assets/MarketplacePage-D0sDlYX4.js +0 -49
  147. package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +0 -40
  148. package/dist/assets/ModelConfig-BzZenCH-.js +0 -1
  149. package/dist/assets/ProvidersList-BbVzRxjY.js +0 -1
  150. package/dist/assets/RemoteAccessPage-BaDH_X1Q.js +0 -1
  151. package/dist/assets/RuntimeConfig-F_XKGgLm.js +0 -1
  152. package/dist/assets/SearchConfig-BGkzXQP-.js +0 -1
  153. package/dist/assets/SessionsConfig-ChHQ7M5c.js +0 -2
  154. package/dist/assets/chat-page-Doe0yTtB.js +0 -58
  155. package/dist/assets/chat-session-display-cw78aiI_.js +0 -1
  156. package/dist/assets/config-layout-CHs0mAaR.js +0 -1
  157. package/dist/assets/desktop-update-config-Dpcf4BKG.js +0 -1
  158. package/dist/assets/index-CF9xve0E.js +0 -6
  159. package/dist/assets/index-FgA52VBt.css +0 -1
  160. package/dist/assets/loader-circle-ACM1s51e.js +0 -1
  161. package/dist/assets/play-CFUwCA2E.js +0 -1
  162. package/dist/assets/plus-rYsv72JG.js +0 -1
  163. package/dist/assets/popover-Bg1VoTZ6.js +0 -1
  164. package/dist/assets/search-3kFR_zh9.js +0 -1
  165. package/dist/assets/security-config-BWaiARNk.js +0 -1
  166. package/dist/assets/select-DJ2MUjBB.js +0 -41
  167. package/dist/assets/skeleton-ByQepn0M.js +0 -1
  168. package/dist/assets/x-ByDbItbq.js +0 -1
  169. package/src/components/agents/AgentDialogs.tsx +0 -400
  170. package/src/components/agents/AgentsPage.test.tsx +0 -217
  171. package/src/components/agents/AgentsPage.tsx +0 -352
  172. package/src/components/config/config-layout.ts +0 -10
  173. package/src/components/marketplace/MarketplacePage.test.tsx +0 -322
  174. package/src/components/marketplace/MarketplacePage.tsx +0 -827
  175. package/src/components/marketplace/mcp/McpMarketplacePage.test.tsx +0 -208
  176. package/src/components/marketplace/mcp/McpMarketplacePage.tsx +0 -580
  177. package/src/components/remote/RemoteAccessPage.test.tsx +0 -103
  178. package/src/components/remote/RemoteAccessPage.tsx +0 -144
@@ -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;
package/src/api/types.ts CHANGED
@@ -246,9 +246,16 @@ 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
+
249
255
  export type RuntimeEntryView = {
250
256
  enabled?: boolean;
251
257
  label?: string;
258
+ icon?: SessionTypeIconView | null;
252
259
  type: string;
253
260
  config?: Record<string, unknown>;
254
261
  };
@@ -677,6 +684,7 @@ export type ConfigActionExecuteResult = {
677
684
  // WebSocket events
678
685
  export type WsEvent =
679
686
  | { type: 'config.updated'; payload: { path: string } }
687
+ | { type: 'channel.config.apply-status'; payload: { channel: string; status: 'started' | 'succeeded' | 'failed'; message?: string } }
680
688
  | { type: 'session.updated'; payload: { sessionKey: string } }
681
689
  | { type: 'session.run-status'; payload: { sessionKey: string; status: 'running' | 'idle' } }
682
690
  | { type: 'session.summary.upsert'; payload: { summary: NcpSessionSummaryView } }