@nextclaw/ui 0.12.9 → 0.12.11

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 (245) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/dist/assets/ChannelsList-SQ7Oxotv.js +8 -0
  3. package/dist/assets/DocBrowser-BCO2k6XD.js +1 -0
  4. package/dist/assets/{DocBrowser-6ReNjvzF.js → DocBrowser-rDOjI3ga.js} +1 -1
  5. package/dist/assets/{DocBrowserContext-B6SpA7Qs.js → DocBrowserContext-BUq3Wo8O.js} +1 -1
  6. package/dist/assets/{LogoBadge-ByNLYg65.js → LogoBadge-DP8Ye7wJ.js} +1 -1
  7. package/dist/assets/ModelConfig-C77Ae9ru.js +1 -0
  8. package/dist/assets/ProviderScopedModelInput-CEnK61uo.js +1 -0
  9. package/dist/assets/ProvidersList-BCupBayq.js +1 -0
  10. package/dist/assets/RuntimeConfig-Ad-CAcmy.js +1 -0
  11. package/dist/assets/SearchConfig-BfCz4wJ4.js +1 -0
  12. package/dist/assets/SecretsConfig-DjmBIhyy.js +3 -0
  13. package/dist/assets/{SessionsConfig-ChHQ7M5c.js → SessionsConfig-CvjxU40H.js} +2 -2
  14. package/dist/assets/{book-open-BdcxxoQu.js → book-open-BE8M56IM.js} +1 -1
  15. package/dist/assets/chat-page-JKC6ln-y.js +58 -0
  16. package/dist/assets/chat-session-display-YcRMrAMa.js +1 -0
  17. package/dist/assets/{chunk-JZWAC4HX-DK5HPmIK.js → chunk-JZWAC4HX-erTUn3b8.js} +1 -1
  18. package/dist/assets/client-CszWMVKi.js +7 -0
  19. package/dist/assets/config-split-page-BAGSzUR3.js +1 -0
  20. package/dist/assets/{createLucideIcon-BSeTgkZW.js → createLucideIcon-CCiTGX8L.js} +1 -1
  21. package/dist/assets/desktop-DfkLlkG2.js +1 -0
  22. package/dist/assets/desktop-update-config-BXeGlqHD.js +1 -0
  23. package/dist/assets/dialog-BghZFPch.js +5 -0
  24. package/dist/assets/{dist-6TrrnPCR.js → dist-Dd9cr-kz.js} +1 -1
  25. package/dist/assets/dist-ZwoAXs46.js +9 -0
  26. package/dist/assets/{download-BhDxnyvU.js → download-D7LOizcW.js} +1 -1
  27. package/dist/assets/es2015-CEAreese.js +41 -0
  28. package/dist/assets/{external-link-BgErLCNT.js → external-link-qsnCMhw1.js} +1 -1
  29. package/dist/assets/{hash-Bl7dr_UG.js → hash-0zjWsNl-.js} +1 -1
  30. package/dist/assets/{i18n-eDHeDY0n.js → i18n-DvzXOGQX.js} +1 -1
  31. package/dist/assets/index-DvVTC9FF.css +1 -0
  32. package/dist/assets/index-lr6rQUSd.js +2 -0
  33. package/dist/assets/key-round-BLe9D8ND.js +1 -0
  34. package/dist/assets/loader-circle-wj7kARHv.js +1 -0
  35. package/dist/assets/{logos-x89HbrZ4.js → logos-_v5b2SdG.js} +1 -1
  36. package/dist/assets/marketplace-page-CAAk1Khc.js +1 -0
  37. package/dist/assets/marketplace-page-CfCiq90S.js +49 -0
  38. package/dist/assets/mcp-marketplace-page-D0Pp9Hs-.js +40 -0
  39. package/dist/assets/play-o6NmwGTi.js +1 -0
  40. package/dist/assets/plus-I9pBS4Fl.js +1 -0
  41. package/dist/assets/{refresh-cw-C47QSEwg.js → refresh-cw-MNqgR3LZ.js} +1 -1
  42. package/dist/assets/remote-C9fXm4V5.js +1 -0
  43. package/dist/assets/{save-3S6-H3Xw.js → save-D4bObrmH.js} +1 -1
  44. package/dist/assets/search-DxmL3IWE.js +1 -0
  45. package/dist/assets/security-config-BUm6FFfl.js +1 -0
  46. package/dist/assets/select-BILPf7zs.js +1 -0
  47. package/dist/assets/setting-row-BATDgg4r.js +1 -0
  48. package/dist/assets/skeleton-COKMAnJy.js +1 -0
  49. package/dist/assets/{switch-BsLtHOH-.js → switch-CBOzecWS.js} +1 -1
  50. package/dist/assets/{tabs-custom-D3HYMt6k.js → tabs-custom-Bx3cNhD-.js} +1 -1
  51. package/dist/assets/tag-chip-zUaDE2-H.js +1 -0
  52. package/dist/assets/{trash-2-G48scll7.js → trash-2-CQUgYyRn.js} +1 -1
  53. package/dist/assets/use-infinite-scroll-loader-B5V2Klve.js +1 -0
  54. package/dist/assets/useConfirmDialog-patAnl1g.js +1 -0
  55. package/dist/assets/{useMutation-CBWjE2uj.js → useMutation-__AYv-Pz.js} +1 -1
  56. package/dist/assets/x-BHUGQIUv.js +1 -0
  57. package/dist/index.html +22 -22
  58. package/dist/runtime-icons/claude.ico +0 -0
  59. package/dist/runtime-icons/codex-openai.svg +6 -0
  60. package/dist/runtime-icons/hermes-agent.png +0 -0
  61. package/module-structure.config.json +7 -0
  62. package/package.json +6 -6
  63. package/public/runtime-icons/claude.ico +0 -0
  64. package/public/runtime-icons/codex-openai.svg +6 -0
  65. package/public/runtime-icons/hermes-agent.png +0 -0
  66. package/src/api/chat-session-type.types.ts +7 -0
  67. package/src/api/config.ts +10 -0
  68. package/src/api/raw-client.test.ts +1 -1
  69. package/src/api/{raw-client.ts → raw-client.utils.ts} +2 -0
  70. package/src/api/runtime-control.types.ts +8 -0
  71. package/src/api/types.ts +48 -0
  72. package/src/app/components/app-manager-provider.tsx +20 -0
  73. package/src/app/managers/app.manager.ts +12 -0
  74. package/src/app.tsx +223 -59
  75. package/src/components/agents/agent-dialogs.tsx +499 -0
  76. package/src/components/agents/agents-page.test.tsx +238 -0
  77. package/src/components/agents/agents-page.tsx +435 -0
  78. package/src/components/chat/chat-conversation-panel.test.tsx +30 -0
  79. package/src/components/chat/chat-conversation-panel.tsx +83 -13
  80. package/src/components/chat/chat-input/ncp-chat-input-availability.utils.test.ts +92 -0
  81. package/src/components/chat/chat-input/ncp-chat-input-availability.utils.ts +45 -0
  82. package/src/components/chat/chat-page-shell.tsx +19 -13
  83. package/src/components/chat/chat-session-type-option-item.test.tsx +46 -0
  84. package/src/components/chat/chat-session-type-option-item.tsx +68 -0
  85. package/src/components/chat/chat-session-workspace-file-preview.test.tsx +87 -0
  86. package/src/components/chat/chat-session-workspace-file-preview.tsx +14 -43
  87. package/src/components/chat/chat-session-workspace-panel-nav.tsx +8 -2
  88. package/src/components/chat/chat-sidebar-project-groups.tsx +11 -36
  89. package/src/components/chat/containers/chat-input-bar.container.tsx +24 -12
  90. package/src/components/chat/{ChatSidebar.test.tsx → containers/chat-sidebar.test.tsx} +5 -4
  91. package/src/components/chat/{ChatSidebar.tsx → containers/chat-sidebar.tsx} +24 -72
  92. package/src/components/chat/hooks/use-chat-sidebar-session-label-editor.ts +49 -0
  93. package/src/components/chat/ncp/__tests__/ncp-session-adapter.cancelled-tool.test.ts +77 -0
  94. package/src/components/chat/ncp/ncp-app-client-fetch.ts +3 -0
  95. package/src/components/chat/ncp/ncp-chat-input.manager.ts +13 -5
  96. package/src/components/chat/ncp/ncp-chat-page.tsx +23 -2
  97. package/src/components/chat/ncp/ncp-session-adapter.test.ts +1 -0
  98. package/src/components/chat/ncp/ncp-session-adapter.ts +3 -0
  99. package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +10 -4
  100. package/src/components/chat/ncp/session-conversation/use-ncp-session-conversation.test.tsx +48 -4
  101. package/src/components/chat/ncp/session-conversation/use-ncp-session-conversation.ts +43 -5
  102. package/src/components/chat/ncp/tests/ncp-chat-input.manager.test.ts +51 -1
  103. package/src/components/chat/stores/chat-input.store.ts +2 -1
  104. package/src/components/chat/stores/chat-thread.store.ts +3 -1
  105. package/src/components/chat/useChatSessionTypeState.ts +10 -1
  106. package/src/components/chat/workspace/chat-session-workspace-file-breadcrumbs.tsx +86 -0
  107. package/src/components/common/BrandHeader.tsx +3 -1
  108. package/src/components/common/session-context-icon.tsx +15 -2
  109. package/src/components/common/{TagInput.tsx → tag-input.tsx} +25 -17
  110. package/src/components/config/ChannelForm.test.tsx +89 -3
  111. package/src/components/config/ChannelForm.tsx +157 -188
  112. package/src/components/config/ChannelsList.test.tsx +163 -119
  113. package/src/components/config/ChannelsList.tsx +90 -101
  114. package/src/components/config/ProviderForm.tsx +108 -146
  115. package/src/components/config/ProvidersList.tsx +100 -123
  116. package/src/components/config/SearchConfig.tsx +423 -393
  117. package/src/components/config/channel-form-fields-section.tsx +70 -37
  118. package/src/components/config/config-split-page.tsx +109 -0
  119. package/src/components/config/desktop-update-config.test.tsx +10 -4
  120. package/src/components/config/desktop-update-config.tsx +5 -3
  121. package/src/components/config/provider-enabled-field.tsx +17 -10
  122. package/src/components/config/runtime-control-card.test.tsx +136 -158
  123. package/src/components/config/runtime-control-card.tsx +43 -68
  124. package/src/components/config/runtime-presence-card.test.tsx +10 -14
  125. package/src/components/config/runtime-presence-card.tsx +97 -81
  126. package/src/components/layout/AppLayout.tsx +25 -37
  127. package/src/components/layout/Sidebar.tsx +4 -4
  128. package/src/components/layout/app-layout.test.tsx +46 -14
  129. package/src/components/layout/runtime-status-entry.test.tsx +101 -0
  130. package/src/components/layout/runtime-status-entry.tsx +95 -0
  131. package/src/components/layout/sidebar.layout.test.tsx +11 -5
  132. package/src/components/marketplace/marketplace-detail-doc.ts +93 -0
  133. package/src/components/marketplace/marketplace-list-card.tsx +288 -0
  134. package/src/components/marketplace/marketplace-page-data.ts +129 -0
  135. package/src/components/marketplace/marketplace-page.test.tsx +339 -0
  136. package/src/components/marketplace/marketplace-page.tsx +596 -0
  137. package/src/components/marketplace/mcp/mcp-marketplace-card.tsx +128 -0
  138. package/src/components/marketplace/mcp/mcp-marketplace-dialogs.tsx +191 -0
  139. package/src/components/marketplace/mcp/mcp-marketplace-doc.ts +152 -0
  140. package/src/components/marketplace/mcp/mcp-marketplace-page.test.tsx +223 -0
  141. package/src/components/marketplace/mcp/mcp-marketplace-page.tsx +414 -0
  142. package/src/components/ui/notice-card.tsx +129 -0
  143. package/src/components/ui/setting-row.tsx +51 -0
  144. package/src/components/ui/tag-chip.tsx +39 -0
  145. package/src/components/ui/textarea.tsx +19 -0
  146. package/src/features/account/components/account-panel.tsx +255 -0
  147. package/src/features/account/index.ts +6 -0
  148. package/src/{account → features/account}/managers/account.manager.ts +6 -5
  149. package/src/features/remote/components/remote-access-page.test.tsx +104 -0
  150. package/src/features/remote/components/remote-access-page.tsx +250 -0
  151. package/src/{hooks/useRemoteAccess.ts → features/remote/hooks/use-remote-access.ts} +1 -1
  152. package/src/features/remote/index.ts +27 -0
  153. package/src/{remote → features/remote}/managers/remote-access.manager.ts +3 -4
  154. package/src/{remote → features/remote/services}/remote-access-feedback.service.test.ts +1 -1
  155. package/src/features/system-status/hooks/use-system-status.ts +104 -0
  156. package/src/features/system-status/index.ts +12 -0
  157. package/src/features/system-status/managers/system-status.manager.bootstrap-polling.test.ts +126 -0
  158. package/src/features/system-status/managers/system-status.manager.test.ts +142 -0
  159. package/src/features/system-status/managers/system-status.manager.ts +511 -0
  160. package/src/features/system-status/stores/system-status.store.ts +32 -0
  161. package/src/features/system-status/types/system-status.types.ts +73 -0
  162. package/src/features/system-status/utils/system-status.utils.test.ts +132 -0
  163. package/src/features/system-status/utils/system-status.utils.ts +202 -0
  164. package/src/hooks/use-realtime-query-bridge.ts +34 -18
  165. package/src/hooks/useConfig.ts +2 -1
  166. package/src/index.css +24 -0
  167. package/src/lib/app-resource-uri.test.ts +20 -0
  168. package/src/lib/app-resource-uri.ts +29 -0
  169. package/src/lib/i18n.chat.ts +8 -0
  170. package/src/lib/i18n.remote.ts +1 -1
  171. package/src/lib/i18n.runtime-control.ts +31 -0
  172. package/src/lib/i18n.ts +5 -8
  173. package/src/lib/session-context.utils.test.ts +71 -0
  174. package/src/lib/session-context.utils.ts +28 -3
  175. package/src/lib/session-project/workspace-file-breadcrumb.test.ts +83 -0
  176. package/src/lib/session-project/workspace-file-breadcrumb.ts +188 -0
  177. package/src/platforms/desktop/index.ts +20 -0
  178. package/src/{desktop → platforms/desktop}/managers/desktop-presence.manager.ts +2 -2
  179. package/src/{desktop → platforms/desktop}/managers/desktop-update.manager.ts +2 -2
  180. package/src/{desktop → platforms/desktop}/stores/desktop-presence.store.ts +1 -1
  181. package/src/{desktop → platforms/desktop}/stores/desktop-update.store.ts +1 -1
  182. package/src/stores/ui.store.ts +0 -9
  183. package/src/transport/{app-client.ts → app-client.service.ts} +9 -9
  184. package/src/transport/app-client.test.ts +9 -5
  185. package/src/transport/index.ts +1 -1
  186. package/src/transport/{local.transport.ts → local-transport.service.ts} +14 -12
  187. package/dist/assets/ChannelsList-Ita2Zm1_.js +0 -8
  188. package/dist/assets/DocBrowser-BNwbPHf4.js +0 -1
  189. package/dist/assets/MarketplacePage-CjX2MWww.js +0 -1
  190. package/dist/assets/MarketplacePage-D0sDlYX4.js +0 -49
  191. package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +0 -40
  192. package/dist/assets/ModelConfig-BzZenCH-.js +0 -1
  193. package/dist/assets/ProviderScopedModelInput-Da7khnBA.js +0 -1
  194. package/dist/assets/ProvidersList-BbVzRxjY.js +0 -1
  195. package/dist/assets/RemoteAccessPage-BaDH_X1Q.js +0 -1
  196. package/dist/assets/RuntimeConfig-F_XKGgLm.js +0 -1
  197. package/dist/assets/SearchConfig-BGkzXQP-.js +0 -1
  198. package/dist/assets/SecretsConfig-D281Rotl.js +0 -3
  199. package/dist/assets/app-query-client-VnFElj4E.js +0 -1
  200. package/dist/assets/chat-page-Doe0yTtB.js +0 -58
  201. package/dist/assets/chat-session-display-cw78aiI_.js +0 -1
  202. package/dist/assets/client-_i4MU2bB.js +0 -7
  203. package/dist/assets/config-DtIQwrHF.js +0 -1
  204. package/dist/assets/config-layout-CHs0mAaR.js +0 -1
  205. package/dist/assets/desktop-update-config-Dpcf4BKG.js +0 -1
  206. package/dist/assets/dist-ccBFUi-o.js +0 -9
  207. package/dist/assets/index-CF9xve0E.js +0 -6
  208. package/dist/assets/index-FgA52VBt.css +0 -1
  209. package/dist/assets/infiniteQueryBehavior-ZDS92Qpp.js +0 -1
  210. package/dist/assets/loader-circle-ACM1s51e.js +0 -1
  211. package/dist/assets/page-layout-vZnghcFy.js +0 -1
  212. package/dist/assets/play-CFUwCA2E.js +0 -1
  213. package/dist/assets/plus-rYsv72JG.js +0 -1
  214. package/dist/assets/popover-Bg1VoTZ6.js +0 -1
  215. package/dist/assets/refresh-ccw-DT98i__E.js +0 -1
  216. package/dist/assets/rotate-cw-JtFzpNn6.js +0 -1
  217. package/dist/assets/search-3kFR_zh9.js +0 -1
  218. package/dist/assets/security-config-BWaiARNk.js +0 -1
  219. package/dist/assets/select-DJ2MUjBB.js +0 -41
  220. package/dist/assets/skeleton-ByQepn0M.js +0 -1
  221. package/dist/assets/status-dot-vbanNPFU.js +0 -1
  222. package/dist/assets/use-infinite-scroll-loader-DkNhD-42.js +0 -1
  223. package/dist/assets/useConfirmDialog-BkvTN-vd.js +0 -1
  224. package/dist/assets/x-ByDbItbq.js +0 -1
  225. package/src/account/components/account-panel.tsx +0 -135
  226. package/src/components/agents/AgentDialogs.tsx +0 -400
  227. package/src/components/agents/AgentsPage.test.tsx +0 -217
  228. package/src/components/agents/AgentsPage.tsx +0 -352
  229. package/src/components/config/config-layout.ts +0 -10
  230. package/src/components/marketplace/MarketplacePage.test.tsx +0 -322
  231. package/src/components/marketplace/MarketplacePage.tsx +0 -827
  232. package/src/components/marketplace/mcp/McpMarketplacePage.test.tsx +0 -208
  233. package/src/components/marketplace/mcp/McpMarketplacePage.tsx +0 -580
  234. package/src/components/remote/RemoteAccessPage.test.tsx +0 -103
  235. package/src/components/remote/RemoteAccessPage.tsx +0 -144
  236. package/src/hooks/use-runtime-control.ts +0 -24
  237. package/src/presenter/app-presenter-context.tsx +0 -20
  238. package/src/presenter/app.presenter.ts +0 -12
  239. package/src/runtime-control/runtime-control.manager.ts +0 -118
  240. /package/dist/assets/{config-hints-BhTmc9P1.js → config-hints-DSQQbeOA.js} +0 -0
  241. /package/src/{account → features/account}/stores/account.store.ts +0 -0
  242. /package/src/{remote → features/remote/services}/remote-access-feedback.service.ts +0 -0
  243. /package/src/{remote/remote-access.query.ts → features/remote/services/remote-access-query.service.ts} +0 -0
  244. /package/src/{remote → features/remote}/stores/remote-access.store.ts +0 -0
  245. /package/src/{desktop → platforms/desktop/types}/desktop-update.types.ts +0 -0
@@ -1,144 +0,0 @@
1
- import { PageHeader, PageLayout } from '@/components/layout/page-layout';
2
- import { Button } from '@/components/ui/button';
3
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
4
- import { StatusDot } from '@/components/ui/status-dot';
5
- import { useRemoteStatus } from '@/hooks/useRemoteAccess';
6
- import { formatDateTime, t } from '@/lib/i18n';
7
- import { useAppPresenter } from '@/presenter/app-presenter-context';
8
- import { resolveRemoteWebBase } from '@/remote/remote-access.query';
9
- import { buildRemoteAccessFeedbackView } from '@/remote/remote-access-feedback.service';
10
- import { useRemoteAccessStore } from '@/remote/stores/remote-access.store';
11
- import { Laptop, RefreshCcw, SquareArrowOutUpRight } from 'lucide-react';
12
- import { useEffect, useMemo } from 'react';
13
-
14
- function KeyValueRow(props: { label: string; value?: string | number | null; muted?: boolean }) {
15
- const { label, muted, value: rawValue } = props;
16
- const value = rawValue === undefined || rawValue === null || rawValue === '' ? '-' : String(rawValue);
17
- return (
18
- <div className="flex items-start justify-between gap-4 py-2 text-sm">
19
- <span className="text-gray-500">{label}</span>
20
- <span className={muted ? 'text-right text-gray-500' : 'text-right text-gray-900'}>{value}</span>
21
- </div>
22
- );
23
- }
24
-
25
-
26
- export function RemoteAccessPage() {
27
- const presenter = useAppPresenter();
28
- const remoteStatus = useRemoteStatus();
29
- const status = remoteStatus.data;
30
- const actionLabel = useRemoteAccessStore((state) => state.actionLabel);
31
- const feedbackView = useMemo(() => buildRemoteAccessFeedbackView(status), [status]);
32
- const busy = Boolean(actionLabel);
33
- const deviceName = status?.runtime?.deviceName?.trim() || status?.settings.deviceName?.trim() || t('remoteDeviceNameAuto');
34
- const canOpenDeviceList = Boolean(status?.account.loggedIn && resolveRemoteWebBase(status));
35
- const { hero: heroView, issueHint } = feedbackView;
36
-
37
- useEffect(() => {
38
- presenter.remoteAccessManager.syncStatus(status);
39
- }, [presenter, status]);
40
-
41
- if (remoteStatus.isLoading && !status) {
42
- return <div className="p-8 text-gray-400">{t('remoteLoading')}</div>;
43
- }
44
-
45
- return (
46
- <PageLayout className="space-y-6">
47
- <PageHeader title={t('remotePageTitle')} description={t('remotePageDescription')} />
48
-
49
- <div className="grid gap-6 xl:grid-cols-[1.2fr_0.8fr]">
50
- <Card>
51
- <CardHeader className="space-y-4">
52
- <div className="flex flex-wrap items-center gap-3">
53
- <CardTitle>{heroView.title}</CardTitle>
54
- <StatusDot status={heroView.badgeStatus} label={heroView.badgeLabel} />
55
- </div>
56
- <CardDescription>{heroView.description}</CardDescription>
57
- </CardHeader>
58
- <CardContent className="space-y-5">
59
- <div className="rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3">
60
- <KeyValueRow label={t('remoteSignedInAccount')} value={status?.account.email} />
61
- <KeyValueRow label={t('remoteDeviceName')} value={deviceName} />
62
- <KeyValueRow label={t('remoteConnectionStatus')} value={heroView.badgeLabel} />
63
- <KeyValueRow label={t('remoteLastConnectedAt')} value={status?.runtime?.lastConnectedAt ? formatDateTime(status.runtime.lastConnectedAt) : '-'} muted />
64
- </div>
65
-
66
- <div className="flex flex-wrap gap-3">
67
- {feedbackView.primaryAction ? (
68
- <Button
69
- onClick={() => {
70
- if (feedbackView.primaryAction?.kind === 'reauthorize') {
71
- void presenter.remoteAccessManager.reauthorizeRemoteAccess(status);
72
- return;
73
- }
74
- if (feedbackView.primaryAction?.kind === 'repair') {
75
- void presenter.remoteAccessManager.repairRemoteAccess(status);
76
- return;
77
- }
78
- void presenter.remoteAccessManager.enableRemoteAccess(status);
79
- }}
80
- disabled={busy}
81
- >
82
- {feedbackView.primaryAction.showRefreshIcon ? <RefreshCcw className="mr-2 h-4 w-4" /> : null}
83
- {actionLabel || feedbackView.primaryAction.label}
84
- </Button>
85
- ) : null}
86
-
87
- <Button
88
- variant="outline"
89
- onClick={() => void presenter.accountManager.openNextClawWeb()}
90
- disabled={busy || !canOpenDeviceList}
91
- >
92
- <SquareArrowOutUpRight className="mr-2 h-4 w-4" />
93
- {t('remoteOpenDeviceList')}
94
- </Button>
95
-
96
- {status?.settings.enabled ? (
97
- <Button variant="outline" onClick={() => void presenter.remoteAccessManager.disableRemoteAccess(status)} disabled={busy}>
98
- {t('remoteDisable')}
99
- </Button>
100
- ) : null}
101
- </div>
102
-
103
- {feedbackView.shouldShowIssueHint && issueHint ? (
104
- <div className="rounded-2xl border border-amber-200 bg-amber-50 px-4 py-3">
105
- <p className="text-sm font-medium text-amber-900">{issueHint.title}</p>
106
- <p className="mt-1 text-sm leading-6 text-amber-800">{issueHint.body}</p>
107
- </div>
108
- ) : null}
109
-
110
- <p className="text-xs text-gray-500">{t('remoteOpenWebHint')}</p>
111
- </CardContent>
112
- </Card>
113
-
114
- <Card>
115
- <CardHeader>
116
- <CardTitle className="flex items-center gap-2">
117
- <Laptop className="h-4 w-4 text-primary" />
118
- {t('remoteDeviceSectionTitle')}
119
- </CardTitle>
120
- <CardDescription>{t('remoteDeviceSectionDescription')}</CardDescription>
121
- </CardHeader>
122
- <CardContent className="space-y-5">
123
- <div className="flex flex-wrap gap-2">
124
- <StatusDot status={status?.account.loggedIn ? 'ready' : 'inactive'} label={status?.account.loggedIn ? t('remoteAccountConnected') : t('remoteAccountNotConnected')} />
125
- <StatusDot status={status?.settings.enabled ? 'active' : 'inactive'} label={status?.settings.enabled ? t('remoteEnabled') : t('remoteStateDisabled')} />
126
- <StatusDot status={status?.service.running ? 'active' : 'inactive'} label={status?.service.running ? t('remoteServiceRunning') : t('remoteServiceStopped')} />
127
- </div>
128
-
129
- <div className="rounded-2xl border border-gray-200 bg-gray-50 px-4 py-3">
130
- <KeyValueRow label={t('remoteDeviceName')} value={deviceName} />
131
- <KeyValueRow label={t('remoteConnectionStatus')} value={heroView.badgeLabel} />
132
- <KeyValueRow label={t('remoteLastConnectedAt')} value={status?.runtime?.lastConnectedAt ? formatDateTime(status.runtime.lastConnectedAt) : '-'} muted />
133
- </div>
134
-
135
- <div className="rounded-2xl border border-dashed border-gray-200 bg-gray-50 px-4 py-3 text-sm text-gray-600">
136
- {status?.account.loggedIn ? t('remoteOpenWebHint') : t('remoteStatusNeedsSignInDescription')}
137
- </div>
138
- </CardContent>
139
- </Card>
140
- </div>
141
-
142
- </PageLayout>
143
- );
144
- }
@@ -1,24 +0,0 @@
1
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
- import type { RuntimeControlView } from '@/api/runtime-control.types';
3
- import { runtimeControlManager } from '@/runtime-control/runtime-control.manager';
4
-
5
- export function useRuntimeControl() {
6
- return useQuery({
7
- queryKey: ['runtime-control'],
8
- queryFn: async (): Promise<RuntimeControlView> => await runtimeControlManager.getControl(),
9
- staleTime: 5_000,
10
- refetchOnWindowFocus: true
11
- });
12
- }
13
-
14
- export function useRuntimeServiceAction() {
15
- const queryClient = useQueryClient();
16
-
17
- return useMutation({
18
- mutationFn: async (action: 'start-service' | 'restart-service' | 'stop-service') =>
19
- await runtimeControlManager.controlService(action),
20
- onSuccess: async () => {
21
- await queryClient.invalidateQueries({ queryKey: ['runtime-control'] });
22
- }
23
- });
24
- }
@@ -1,20 +0,0 @@
1
- import { createContext, useContext, type ReactNode } from 'react';
2
- import { appPresenter, type AppPresenter } from '@/presenter/app.presenter';
3
-
4
- const AppPresenterContext = createContext<AppPresenter | null>(null);
5
-
6
- type AppPresenterProviderProps = {
7
- children: ReactNode;
8
- };
9
-
10
- export function AppPresenterProvider({ children }: AppPresenterProviderProps) {
11
- return <AppPresenterContext.Provider value={appPresenter}>{children}</AppPresenterContext.Provider>;
12
- }
13
-
14
- export function useAppPresenter() {
15
- const presenter = useContext(AppPresenterContext);
16
- if (!presenter) {
17
- throw new Error('useAppPresenter must be used inside AppPresenterProvider');
18
- }
19
- return presenter;
20
- }
@@ -1,12 +0,0 @@
1
- import { AccountManager } from '@/account/managers/account.manager';
2
- import { RemoteAccessManager } from '@/remote/managers/remote-access.manager';
3
-
4
- export class AppPresenter {
5
- accountManager = new AccountManager();
6
- remoteAccessManager = new RemoteAccessManager();
7
- }
8
-
9
- export const appPresenter = new AppPresenter();
10
-
11
- appPresenter.accountManager.bindSignedInContinuation(appPresenter.remoteAccessManager.resumePendingActionAfterSignIn);
12
- appPresenter.remoteAccessManager.bindAccountManager(appPresenter.accountManager);
@@ -1,118 +0,0 @@
1
- import { fetchRuntimeControl, restartRuntimeService, startRuntimeService, stopRuntimeService } from '@/api/runtime-control';
2
- import type {
3
- RuntimeControlAction,
4
- RuntimeControlActionResult,
5
- RuntimeControlView
6
- } from '@/api/runtime-control.types';
7
- import type { NextClawDesktopBridge } from '@/desktop/desktop-update.types';
8
- import { t } from '@/lib/i18n';
9
-
10
- type RecoveryWaitOptions = {
11
- timeoutMs?: number;
12
- pollIntervalMs?: number;
13
- };
14
-
15
- export class RuntimeControlManager {
16
- getControl = async (): Promise<RuntimeControlView> => {
17
- return this.decorateForCurrentEnvironment(await fetchRuntimeControl());
18
- };
19
-
20
- controlService = async (action: RuntimeControlAction): Promise<RuntimeControlActionResult> => {
21
- const desktopBridge = this.getDesktopBridge();
22
- if (action === 'restart-service' && desktopBridge && typeof desktopBridge.restartService === 'function') {
23
- const result = await desktopBridge.restartService();
24
- return {
25
- accepted: result.accepted,
26
- action: 'restart-service',
27
- lifecycle: result.lifecycle,
28
- message: result.message
29
- };
30
- }
31
- if (action === 'start-service') {
32
- return await startRuntimeService();
33
- }
34
- if (action === 'stop-service') {
35
- return await stopRuntimeService();
36
- }
37
- return await restartRuntimeService();
38
- };
39
-
40
- restartApp = async (): Promise<RuntimeControlActionResult> => {
41
- const desktopBridge = this.getDesktopBridge();
42
- if (!desktopBridge || typeof desktopBridge.restartApp !== 'function') {
43
- throw new Error(t('runtimeRestartAppUnavailable'));
44
- }
45
- const result = await desktopBridge.restartApp();
46
- return {
47
- accepted: result.accepted,
48
- action: 'restart-app',
49
- lifecycle: result.lifecycle,
50
- message: result.message
51
- };
52
- };
53
-
54
- waitForRecovery = async (options: RecoveryWaitOptions = {}): Promise<RuntimeControlView> => {
55
- const timeoutMs = options.timeoutMs ?? 25_000;
56
- const pollIntervalMs = options.pollIntervalMs ?? 1_500;
57
- const deadline = Date.now() + timeoutMs;
58
-
59
- let lastError: unknown = null;
60
- while (Date.now() < deadline) {
61
- try {
62
- return await this.getControl();
63
- } catch (error) {
64
- lastError = error;
65
- await this.sleep(pollIntervalMs);
66
- }
67
- }
68
-
69
- throw lastError instanceof Error
70
- ? lastError
71
- : new Error(t('runtimeRecoveryTimedOut'));
72
- };
73
-
74
- decorateForCurrentEnvironment = (view: RuntimeControlView): RuntimeControlView => {
75
- const desktopBridge = this.getDesktopBridge();
76
- if (!desktopBridge || typeof desktopBridge.restartApp !== 'function') {
77
- return view;
78
- }
79
-
80
- return {
81
- ...view,
82
- environment: 'desktop-embedded',
83
- serviceState: 'running',
84
- canStartService: {
85
- available: false,
86
- requiresConfirmation: false,
87
- impact: 'none'
88
- },
89
- canStopService: {
90
- available: false,
91
- requiresConfirmation: true,
92
- impact: 'brief-ui-disconnect'
93
- },
94
- canRestartApp: {
95
- available: true,
96
- requiresConfirmation: true,
97
- impact: 'full-app-relaunch'
98
- },
99
- ownerLabel: t('runtimeControlEnvironmentDesktop'),
100
- managementHint: t('runtimeControlDesktopServiceHint')
101
- };
102
- };
103
-
104
- private getDesktopBridge = (): NextClawDesktopBridge | null => {
105
- if (typeof window === 'undefined') {
106
- return null;
107
- }
108
- return window.nextclawDesktop ?? null;
109
- };
110
-
111
- private sleep = async (ms: number): Promise<void> => {
112
- await new Promise<void>((resolve) => {
113
- window.setTimeout(resolve, ms);
114
- });
115
- };
116
- }
117
-
118
- export const runtimeControlManager = new RuntimeControlManager();