@nextclaw/ui 0.12.7 → 0.12.9

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 (171) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/dist/assets/ChannelsList-Ita2Zm1_.js +8 -0
  3. package/dist/assets/{DocBrowser-Cse_F8Nn.js → DocBrowser-6ReNjvzF.js} +1 -1
  4. package/dist/assets/DocBrowser-BNwbPHf4.js +1 -0
  5. package/dist/assets/{DocBrowserContext-Bai1WU2H.js → DocBrowserContext-B6SpA7Qs.js} +1 -1
  6. package/dist/assets/{LogoBadge-BdxMPc9v.js → LogoBadge-ByNLYg65.js} +1 -1
  7. package/dist/assets/MarketplacePage-CjX2MWww.js +1 -0
  8. package/dist/assets/{MarketplacePage-BbpAkllU.js → MarketplacePage-D0sDlYX4.js} +1 -1
  9. package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +40 -0
  10. package/dist/assets/{ModelConfig-3GLqQ5GY.js → ModelConfig-BzZenCH-.js} +1 -1
  11. package/dist/assets/{ProviderScopedModelInput-BYNouw-i.js → ProviderScopedModelInput-Da7khnBA.js} +1 -1
  12. package/dist/assets/{ProvidersList-BR1gJ4Dm.js → ProvidersList-BbVzRxjY.js} +1 -1
  13. package/dist/assets/RemoteAccessPage-BaDH_X1Q.js +1 -0
  14. package/dist/assets/RuntimeConfig-F_XKGgLm.js +1 -0
  15. package/dist/assets/{SearchConfig-DTeJvp8m.js → SearchConfig-BGkzXQP-.js} +1 -1
  16. package/dist/assets/{SecretsConfig-CCYO6NcV.js → SecretsConfig-D281Rotl.js} +2 -2
  17. package/dist/assets/{SessionsConfig-Du39vDgt.js → SessionsConfig-ChHQ7M5c.js} +2 -2
  18. package/dist/assets/{app-query-client-Dr5d-K8d.js → app-query-client-VnFElj4E.js} +1 -1
  19. package/dist/assets/{book-open-Da4OEPqB.js → book-open-BdcxxoQu.js} +1 -1
  20. package/dist/assets/chat-page-Doe0yTtB.js +58 -0
  21. package/dist/assets/chat-session-display-cw78aiI_.js +1 -0
  22. package/dist/assets/{chunk-JZWAC4HX-CoFVxHXV.js → chunk-JZWAC4HX-DK5HPmIK.js} +1 -1
  23. package/dist/assets/{client-CSk58DcF.js → client-_i4MU2bB.js} +1 -1
  24. package/dist/assets/{config-D8KzikVB.js → config-DtIQwrHF.js} +1 -1
  25. package/dist/assets/{createLucideIcon-83gaZMtv.js → createLucideIcon-BSeTgkZW.js} +1 -1
  26. package/dist/assets/desktop-update-config-Dpcf4BKG.js +1 -0
  27. package/dist/assets/{dist-toEYs-MZ.js → dist-6TrrnPCR.js} +1 -1
  28. package/dist/assets/{dist-aTmhMDVh.js → dist-ccBFUi-o.js} +1 -1
  29. package/dist/assets/download-BhDxnyvU.js +1 -0
  30. package/dist/assets/{external-link-QQ0TC6X4.js → external-link-BgErLCNT.js} +1 -1
  31. package/dist/assets/{hash-DaFBEkmi.js → hash-Bl7dr_UG.js} +1 -1
  32. package/dist/assets/i18n-eDHeDY0n.js +1 -0
  33. package/dist/assets/index-CF9xve0E.js +6 -0
  34. package/dist/assets/index-FgA52VBt.css +1 -0
  35. package/dist/assets/{infiniteQueryBehavior-BmHX_ayZ.js → infiniteQueryBehavior-ZDS92Qpp.js} +1 -1
  36. package/dist/assets/loader-circle-ACM1s51e.js +1 -0
  37. package/dist/assets/{logos-Dzlz30M3.js → logos-x89HbrZ4.js} +1 -1
  38. package/dist/assets/{page-layout-D2eRufRQ.js → page-layout-vZnghcFy.js} +1 -1
  39. package/dist/assets/play-CFUwCA2E.js +1 -0
  40. package/dist/assets/plus-rYsv72JG.js +1 -0
  41. package/dist/assets/{popover-BSXxm5bj.js → popover-Bg1VoTZ6.js} +1 -1
  42. package/dist/assets/{refresh-ccw-B3zMtN-_.js → refresh-ccw-DT98i__E.js} +1 -1
  43. package/dist/assets/{refresh-cw-DlZkIHnJ.js → refresh-cw-C47QSEwg.js} +1 -1
  44. package/dist/assets/rotate-cw-JtFzpNn6.js +1 -0
  45. package/dist/assets/{save-Us9fg4Sj.js → save-3S6-H3Xw.js} +1 -1
  46. package/dist/assets/search-3kFR_zh9.js +1 -0
  47. package/dist/assets/{security-config-BGWYwxNr.js → security-config-BWaiARNk.js} +1 -1
  48. package/dist/assets/{select-DLYqySQK.js → select-DJ2MUjBB.js} +1 -1
  49. package/dist/assets/skeleton-ByQepn0M.js +1 -0
  50. package/dist/assets/{status-dot-DGayudyB.js → status-dot-vbanNPFU.js} +1 -1
  51. package/dist/assets/{switch-Dz2ScsKx.js → switch-BsLtHOH-.js} +1 -1
  52. package/dist/assets/{tabs-custom-CdKyjiGk.js → tabs-custom-D3HYMt6k.js} +1 -1
  53. package/dist/assets/{trash-2-Db-mZOZs.js → trash-2-G48scll7.js} +1 -1
  54. package/dist/assets/{use-infinite-scroll-loader-DBJX5hj0.js → use-infinite-scroll-loader-DkNhD-42.js} +1 -1
  55. package/dist/assets/{useConfirmDialog-DL0a-oGC.js → useConfirmDialog-BkvTN-vd.js} +1 -1
  56. package/dist/assets/{useMutation-BdZm-9PL.js → useMutation-CBWjE2uj.js} +1 -1
  57. package/dist/assets/x-ByDbItbq.js +1 -0
  58. package/dist/index.html +95 -21
  59. package/dist/manifest.webmanifest +30 -0
  60. package/dist/offline.html +102 -0
  61. package/dist/pwa-192.png +0 -0
  62. package/dist/pwa-512.png +0 -0
  63. package/dist/sw.js +80 -0
  64. package/index.html +73 -1
  65. package/package.json +6 -6
  66. package/public/manifest.webmanifest +30 -0
  67. package/public/offline.html +102 -0
  68. package/public/pwa-192.png +0 -0
  69. package/public/pwa-512.png +0 -0
  70. package/public/sw.js +80 -0
  71. package/src/api/runtime-control.ts +34 -0
  72. package/src/api/runtime-control.types.ts +58 -0
  73. package/src/api/server-path.ts +27 -4
  74. package/src/api/types.ts +30 -10
  75. package/src/{App.test.tsx → app.test.tsx} +1 -1
  76. package/src/{App.tsx → app.tsx} +10 -1
  77. package/src/components/chat/ChatSidebar.test.tsx +79 -8
  78. package/src/components/chat/ChatSidebar.tsx +43 -26
  79. package/src/components/chat/adapters/chat-message.summary-truncation.test.ts +66 -0
  80. package/src/components/chat/adapters/file-operation/card.ts +9 -0
  81. package/src/components/chat/adapters/file-operation/diff.ts +14 -0
  82. package/src/components/chat/{ChatConversationPanel.test.tsx → chat-conversation-panel.test.tsx} +118 -155
  83. package/src/components/chat/chat-conversation-panel.tsx +412 -0
  84. package/src/components/chat/chat-page-runtime.test.ts +1 -1
  85. package/src/components/chat/chat-page-shell.tsx +1 -1
  86. package/src/components/chat/{ChatPage.tsx → chat-page.tsx} +1 -1
  87. package/src/components/chat/chat-session-workspace-file-preview.test.tsx +91 -0
  88. package/src/components/chat/chat-session-workspace-file-preview.tsx +307 -0
  89. package/src/components/chat/chat-session-workspace-panel-nav.tsx +197 -0
  90. package/src/components/chat/chat-session-workspace-panel.tsx +318 -0
  91. package/src/components/chat/chat-sidebar-session-item.tsx +32 -2
  92. package/src/components/chat/containers/chat-message-list.container.test.tsx +49 -0
  93. package/src/components/chat/containers/chat-message-list.container.tsx +4 -0
  94. package/src/components/chat/managers/chat-session-list.manager.test.ts +94 -31
  95. package/src/components/chat/managers/chat-session-list.manager.ts +86 -14
  96. package/src/components/chat/managers/chat-ui.manager.ts +2 -0
  97. package/src/components/chat/ncp/README.md +1 -1
  98. package/src/components/chat/ncp/ncp-chat-input.manager.ts +7 -1
  99. package/src/components/chat/ncp/ncp-chat-page-data.test.ts +1 -1
  100. package/src/components/chat/ncp/{NcpChatPage.tsx → ncp-chat-page.tsx} +7 -7
  101. package/src/components/chat/ncp/ncp-chat-thread.manager.ts +179 -41
  102. package/src/components/chat/ncp/ncp-session-adapter.test.ts +40 -2
  103. package/src/components/chat/ncp/ncp-session-adapter.ts +29 -0
  104. package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +54 -11
  105. package/src/components/chat/ncp/session-conversation/use-ncp-child-session-tabs-view.ts +4 -0
  106. package/src/components/chat/ncp/tests/ncp-chat-input.manager.test.ts +99 -0
  107. package/src/components/chat/ncp/tests/ncp-chat-thread.manager.test.ts +189 -0
  108. package/src/components/chat/presenter/chat-presenter-context.tsx +13 -2
  109. package/src/components/chat/session-header/chat-session-header-actions.test.tsx +26 -0
  110. package/src/components/chat/session-header/chat-session-header-actions.tsx +19 -1
  111. package/src/components/chat/stores/chat-session-list.store.ts +25 -54
  112. package/src/components/chat/stores/chat-thread.store.ts +24 -0
  113. package/src/components/common/ProviderScopedModelInput.tsx +12 -2
  114. package/src/components/config/ModelConfig.test.tsx +108 -2
  115. package/src/components/config/RuntimeConfig.tsx +154 -7
  116. package/src/components/config/desktop-update-config.test.tsx +85 -0
  117. package/src/components/config/desktop-update-config.tsx +44 -3
  118. package/src/components/config/runtime-control-card.test.tsx +255 -0
  119. package/src/components/config/runtime-control-card.tsx +301 -0
  120. package/src/components/config/runtime-presence-card.test.tsx +154 -0
  121. package/src/components/config/runtime-presence-card.tsx +163 -0
  122. package/src/components/layout/AppLayout.tsx +1 -1
  123. package/src/components/providers/ThemeProvider.tsx +5 -0
  124. package/src/desktop/desktop-update.types.ts +25 -0
  125. package/src/desktop/managers/desktop-presence.manager.ts +91 -0
  126. package/src/desktop/managers/desktop-update.manager.ts +37 -1
  127. package/src/desktop/stores/desktop-presence.store.ts +18 -0
  128. package/src/desktop/stores/desktop-update.store.ts +7 -1
  129. package/src/hooks/server-path/use-server-path-read.ts +20 -0
  130. package/src/hooks/use-runtime-control.ts +24 -0
  131. package/src/lib/chat-message.ts +14 -3
  132. package/src/lib/desktop-update-labels.utils.ts +28 -2
  133. package/src/lib/i18n.chat.ts +12 -1
  134. package/src/lib/i18n.pwa.ts +62 -0
  135. package/src/lib/i18n.runtime-control.ts +120 -0
  136. package/src/lib/i18n.ts +4 -6
  137. package/src/main.tsx +1 -1
  138. package/src/pwa/components/pwa-install-entry.test.tsx +110 -0
  139. package/src/pwa/components/pwa-install-entry.tsx +205 -0
  140. package/src/pwa/managers/pwa-install.manager.test.ts +160 -0
  141. package/src/pwa/managers/pwa-install.manager.ts +232 -0
  142. package/src/pwa/managers/pwa-runtime.manager.ts +196 -0
  143. package/src/pwa/managers/pwa-shell-theme.manager.test.ts +30 -0
  144. package/src/pwa/managers/pwa-shell-theme.manager.ts +46 -0
  145. package/src/pwa/pwa-install-banner.storage.ts +55 -0
  146. package/src/pwa/pwa.types.ts +22 -0
  147. package/src/pwa/register-pwa.ts +14 -0
  148. package/src/pwa/stores/pwa.store.ts +17 -0
  149. package/src/runtime-control/runtime-control.manager.ts +118 -0
  150. package/src/vite-env.d.ts +9 -0
  151. package/dist/assets/ChannelsList-D8p4OlM6.js +0 -8
  152. package/dist/assets/ChatPage-A45t1Rmf.js +0 -58
  153. package/dist/assets/DocBrowser-B2MpsnU9.js +0 -1
  154. package/dist/assets/MarketplacePage-BNZ3Jx5d.js +0 -1
  155. package/dist/assets/McpMarketplacePage-CxPFOgxv.js +0 -40
  156. package/dist/assets/RemoteAccessPage-DyYVWsyK.js +0 -1
  157. package/dist/assets/RuntimeConfig-ChdfK4Y_.js +0 -1
  158. package/dist/assets/chat-session-display-CAlPrnlV.js +0 -1
  159. package/dist/assets/desktop-update-config-CfoVwf-w.js +0 -1
  160. package/dist/assets/i18n-C3jb83S6.js +0 -1
  161. package/dist/assets/index-CE4N7ItL.css +0 -1
  162. package/dist/assets/index-riX7Sg0_.js +0 -6
  163. package/dist/assets/loader-circle-BjMg63eu.js +0 -1
  164. package/dist/assets/plus-CIXME2pD.js +0 -1
  165. package/dist/assets/search-B_Qr0f6C.js +0 -1
  166. package/dist/assets/skeleton-CYQJazv6.js +0 -1
  167. package/dist/assets/x-B8Tho_xC.js +0 -1
  168. package/src/components/chat/ChatConversationPanel.tsx +0 -256
  169. package/src/components/chat/chat-child-session-panel.tsx +0 -262
  170. /package/dist/assets/{config-hints-GSUMvmSo.js → config-hints-BhTmc9P1.js} +0 -0
  171. /package/dist/assets/{config-layout-CgBMG7OL.js → config-layout-CHs0mAaR.js} +0 -0
@@ -7,6 +7,8 @@ export type DesktopUpdateStatus =
7
7
  | 'up-to-date'
8
8
  | 'failed';
9
9
 
10
+ export type DesktopReleaseChannel = 'stable' | 'beta';
11
+
10
12
  export type DesktopUpdatePreferences = {
11
13
  automaticChecks: boolean;
12
14
  autoDownload: boolean;
@@ -14,6 +16,7 @@ export type DesktopUpdatePreferences = {
14
16
 
15
17
  export type DesktopUpdateSnapshot = {
16
18
  status: DesktopUpdateStatus;
19
+ channel: DesktopReleaseChannel;
17
20
  launcherVersion: string;
18
21
  currentVersion: string | null;
19
22
  availableVersion: string | null;
@@ -24,6 +27,23 @@ export type DesktopUpdateSnapshot = {
24
27
  preferences: DesktopUpdatePreferences;
25
28
  };
26
29
 
30
+ export type DesktopRuntimeControlResult = {
31
+ accepted: boolean;
32
+ action: 'restart-service' | 'restart-app';
33
+ lifecycle: 'restarting-service' | 'restarting-app';
34
+ message: string;
35
+ };
36
+
37
+ export type DesktopPresencePreferences = {
38
+ closeToBackground: boolean;
39
+ launchAtLogin: boolean;
40
+ };
41
+
42
+ export type DesktopPresenceSnapshot = DesktopPresencePreferences & {
43
+ supportsLaunchAtLogin: boolean;
44
+ launchAtLoginReason: string | null;
45
+ };
46
+
27
47
  export type NextClawDesktopBridge = {
28
48
  platform: string;
29
49
  version: string;
@@ -32,5 +52,10 @@ export type NextClawDesktopBridge = {
32
52
  downloadUpdate: () => Promise<DesktopUpdateSnapshot>;
33
53
  applyDownloadedUpdate: () => Promise<DesktopUpdateSnapshot>;
34
54
  updatePreferences: (preferences: Partial<DesktopUpdatePreferences>) => Promise<DesktopUpdateSnapshot>;
55
+ updateChannel: (channel: DesktopReleaseChannel) => Promise<DesktopUpdateSnapshot>;
56
+ restartService: () => Promise<DesktopRuntimeControlResult>;
57
+ restartApp: () => Promise<DesktopRuntimeControlResult>;
58
+ getPresenceState: () => Promise<DesktopPresenceSnapshot>;
59
+ updatePresencePreferences: (preferences: Partial<DesktopPresencePreferences>) => Promise<DesktopPresenceSnapshot>;
35
60
  onUpdateStateChanged: (listener: (snapshot: DesktopUpdateSnapshot) => void) => () => void;
36
61
  };
@@ -0,0 +1,91 @@
1
+ import type {
2
+ DesktopPresencePreferences,
3
+ NextClawDesktopBridge
4
+ } from '@/desktop/desktop-update.types';
5
+ import { useDesktopPresenceStore } from '@/desktop/stores/desktop-presence.store';
6
+ import { t } from '@/lib/i18n';
7
+ import { toast } from 'sonner';
8
+
9
+ export class DesktopPresenceManager {
10
+ start = async () => {
11
+ const desktopApi = this.getDesktopApi();
12
+ if (!desktopApi) {
13
+ this.markUnsupported();
14
+ return;
15
+ }
16
+
17
+ useDesktopPresenceStore.setState({
18
+ supported: true,
19
+ initialized: false,
20
+ busyAction: 'loading'
21
+ });
22
+
23
+ try {
24
+ const snapshot = await desktopApi.getPresenceState();
25
+ useDesktopPresenceStore.setState({
26
+ supported: true,
27
+ initialized: true,
28
+ busyAction: null,
29
+ snapshot
30
+ });
31
+ } catch (error) {
32
+ useDesktopPresenceStore.setState({
33
+ supported: true,
34
+ initialized: true,
35
+ busyAction: null
36
+ });
37
+ toast.error(`${t('runtimePresenceLoadFailed')}: ${this.getErrorMessage(error)}`);
38
+ }
39
+ };
40
+
41
+ markUnsupported = () => {
42
+ useDesktopPresenceStore.setState({
43
+ supported: false,
44
+ initialized: true,
45
+ busyAction: null,
46
+ snapshot: null
47
+ });
48
+ };
49
+
50
+ updatePreferences = async (preferences: Partial<DesktopPresencePreferences>) => {
51
+ const desktopApi = this.getDesktopApi();
52
+ if (!desktopApi) {
53
+ throw new Error(t('runtimePresenceLaunchAtLoginUnavailable'));
54
+ }
55
+
56
+ useDesktopPresenceStore.setState({
57
+ busyAction: 'saving-preferences'
58
+ });
59
+
60
+ try {
61
+ const snapshot = await desktopApi.updatePresencePreferences(preferences);
62
+ useDesktopPresenceStore.setState({
63
+ supported: true,
64
+ initialized: true,
65
+ snapshot
66
+ });
67
+ toast.success(t('runtimePresenceSaved'));
68
+ return snapshot;
69
+ } catch (error) {
70
+ toast.error(`${t('runtimePresenceSaveFailed')}: ${this.getErrorMessage(error)}`);
71
+ throw error;
72
+ } finally {
73
+ useDesktopPresenceStore.setState({
74
+ busyAction: null
75
+ });
76
+ }
77
+ };
78
+
79
+ private getDesktopApi = (): NextClawDesktopBridge | null => {
80
+ if (typeof window === 'undefined') {
81
+ return null;
82
+ }
83
+ return window.nextclawDesktop ?? null;
84
+ };
85
+
86
+ private getErrorMessage = (error: unknown): string => {
87
+ return error instanceof Error ? error.message : t('error');
88
+ };
89
+ }
90
+
91
+ export const desktopPresenceManager = new DesktopPresenceManager();
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ DesktopReleaseChannel,
2
3
  DesktopUpdatePreferences,
3
4
  DesktopUpdateSnapshot,
4
5
  NextClawDesktopBridge
@@ -7,7 +8,7 @@ import { useDesktopUpdateStore } from '@/desktop/stores/desktop-update.store';
7
8
  import { t } from '@/lib/i18n';
8
9
  import { toast } from 'sonner';
9
10
 
10
- type DesktopUpdateBusyAction = 'checking' | 'downloading' | 'applying' | 'saving-preferences';
11
+ type DesktopUpdateBusyAction = 'checking' | 'downloading' | 'applying' | 'saving-preferences' | 'switching-channel';
11
12
 
12
13
  export class DesktopUpdateManager {
13
14
  private unsubscribe: (() => void) | null = null;
@@ -125,6 +126,37 @@ export class DesktopUpdateManager {
125
126
  }
126
127
  };
127
128
 
129
+ updateChannel = async (channel: DesktopReleaseChannel) => {
130
+ const currentChannel = useDesktopUpdateStore.getState().snapshot?.channel;
131
+ if (currentChannel === channel) {
132
+ return;
133
+ }
134
+
135
+ let snapshot: DesktopUpdateSnapshot;
136
+ try {
137
+ snapshot = await this.runSnapshotCommand(
138
+ 'switching-channel',
139
+ t('desktopUpdatesChannelChangeFailed'),
140
+ async (desktopApi) => await desktopApi.updateChannel(channel)
141
+ );
142
+ } catch {
143
+ return;
144
+ }
145
+
146
+ if (snapshot.status === 'update-available' && snapshot.availableVersion) {
147
+ toast.success(
148
+ t('desktopUpdatesChannelChangedWithUpdate')
149
+ .replace('{channel}', this.getChannelLabel(channel))
150
+ .replace('{version}', snapshot.availableVersion)
151
+ );
152
+ return;
153
+ }
154
+
155
+ toast.success(
156
+ t('desktopUpdatesChannelChanged').replace('{channel}', this.getChannelLabel(channel))
157
+ );
158
+ };
159
+
128
160
  private runSnapshotCommand = async (
129
161
  busyAction: DesktopUpdateBusyAction,
130
162
  fallbackMessage: string,
@@ -158,6 +190,10 @@ export class DesktopUpdateManager {
158
190
  private getErrorMessage = (error: unknown): string => {
159
191
  return error instanceof Error ? error.message : t('error');
160
192
  };
193
+
194
+ private getChannelLabel = (channel: DesktopReleaseChannel): string => {
195
+ return channel === 'beta' ? t('desktopUpdatesChannelBeta') : t('desktopUpdatesChannelStable');
196
+ };
161
197
  }
162
198
 
163
199
  export const desktopUpdateManager = new DesktopUpdateManager();
@@ -0,0 +1,18 @@
1
+ import type { DesktopPresenceSnapshot } from '@/desktop/desktop-update.types';
2
+ import { create } from 'zustand';
3
+
4
+ type DesktopPresenceBusyAction = 'loading' | 'saving-preferences' | null;
5
+
6
+ type DesktopPresenceStoreState = {
7
+ supported: boolean;
8
+ initialized: boolean;
9
+ busyAction: DesktopPresenceBusyAction;
10
+ snapshot: DesktopPresenceSnapshot | null;
11
+ };
12
+
13
+ export const useDesktopPresenceStore = create<DesktopPresenceStoreState>(() => ({
14
+ supported: false,
15
+ initialized: false,
16
+ busyAction: null,
17
+ snapshot: null
18
+ }));
@@ -1,7 +1,13 @@
1
1
  import type { DesktopUpdateSnapshot } from '@/desktop/desktop-update.types';
2
2
  import { create } from 'zustand';
3
3
 
4
- type DesktopUpdateBusyAction = 'checking' | 'downloading' | 'applying' | 'saving-preferences' | null;
4
+ type DesktopUpdateBusyAction =
5
+ | 'checking'
6
+ | 'downloading'
7
+ | 'applying'
8
+ | 'saving-preferences'
9
+ | 'switching-channel'
10
+ | null;
5
11
 
6
12
  type DesktopUpdateStoreState = {
7
13
  supported: boolean;
@@ -0,0 +1,20 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { fetchServerPathRead } from '@/api/server-path';
3
+
4
+ export function useServerPathRead(params: {
5
+ path?: string | null;
6
+ basePath?: string | null;
7
+ enabled?: boolean;
8
+ }) {
9
+ const normalizedPath = params.path?.trim() ?? '';
10
+ return useQuery({
11
+ queryKey: ['server-path-read', normalizedPath, params.basePath ?? null],
12
+ queryFn: () =>
13
+ fetchServerPathRead({
14
+ path: normalizedPath,
15
+ basePath: params.basePath,
16
+ }),
17
+ enabled: (params.enabled ?? true) && normalizedPath.length > 0,
18
+ staleTime: 0,
19
+ });
20
+ }
@@ -0,0 +1,24 @@
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
+ }
@@ -24,6 +24,17 @@ function truncateText(value: string, maxChars = 2400): string {
24
24
  return `${value.slice(0, maxChars)}\n…`;
25
25
  }
26
26
 
27
+ function truncateInlineText(value: string, maxChars = 120): string {
28
+ const normalized = value.replace(/\s+/g, ' ').trim();
29
+ if (normalized.length <= maxChars) {
30
+ return normalized;
31
+ }
32
+ if (maxChars <= 1) {
33
+ return '…';
34
+ }
35
+ return `${normalized.slice(0, maxChars - 1)}…`;
36
+ }
37
+
27
38
  export function stringifyUnknown(value: unknown): string {
28
39
  if (typeof value === 'string') {
29
40
  return value;
@@ -64,7 +75,7 @@ export function summarizeToolArgs(args: unknown): string | undefined {
64
75
  const parsed = parseArgsObject(args);
65
76
  if (!parsed) {
66
77
  const text = stringifyUnknown(args).trim();
67
- return text ? truncateText(text, 120) : undefined;
78
+ return text ? truncateInlineText(text, 120) : undefined;
68
79
  }
69
80
 
70
81
  const items: string[] = [];
@@ -80,9 +91,9 @@ export function summarizeToolArgs(args: unknown): string | undefined {
80
91
  }
81
92
  }
82
93
  if (items.length > 0) {
83
- return items.join(' · ');
94
+ return truncateInlineText(items.join(' · '), 120);
84
95
  }
85
- return truncateText(stringifyUnknown(parsed), 140);
96
+ return truncateInlineText(stringifyUnknown(parsed), 140);
86
97
  }
87
98
 
88
99
  function toToolName(value: unknown): string {
@@ -28,9 +28,10 @@ export const DESKTOP_UPDATE_LABELS: Record<string, { zh: string; en: string }> =
28
28
  desktopUpdatesStatusUpToDate: { zh: '已是最新', en: 'Up to Date' },
29
29
  desktopUpdatesStatusFailed: { zh: '更新失败', en: 'Failed' },
30
30
  desktopUpdatesLauncherVersion: { zh: '桌面壳版本', en: 'Launcher Version' },
31
- desktopUpdatesCurrentBundleVersion: { zh: '当前产品版本', en: 'Current Product Version' },
31
+ desktopUpdatesCurrentBundleVersion: { zh: '当前内核版本', en: 'Current Kernel Version' },
32
32
  desktopUpdatesAvailableVersion: { zh: '可用版本', en: 'Available Version' },
33
33
  desktopUpdatesLastCheckedAt: { zh: '上次检查时间', en: 'Last Checked' },
34
+ desktopUpdatesCurrentChannel: { zh: '当前更新通道', en: 'Current Release Channel' },
34
35
  desktopUpdatesDownloadedBannerTitle: { zh: '更新已就绪', en: 'Update Ready' },
35
36
  desktopUpdatesDownloadedBannerDescription: {
36
37
  zh: '版本 {version} 已下载完成,等你确认后即可重启应用并完成更新。',
@@ -41,6 +42,22 @@ export const DESKTOP_UPDATE_LABELS: Record<string, { zh: string; en: string }> =
41
42
  zh: '默认自动检查更新,但是否后台下载由你决定。',
42
43
  en: 'Automatic checks stay on by default, while background download remains under your control.'
43
44
  },
45
+ desktopUpdatesReleaseChannel: { zh: '更新通道', en: 'Release Channel' },
46
+ desktopUpdatesReleaseChannelHelp: {
47
+ zh: 'Stable 面向日常主力使用;Beta 用于提前体验新版本,但可能更不稳定。',
48
+ en: 'Stable is for everyday use, while Beta lets you try newer builds earlier with more risk.'
49
+ },
50
+ desktopUpdatesReleaseChannelDowngradeHint: {
51
+ zh: '切回 Stable 后不会立刻强制降级;只有当 Stable 追平或超过当前版本时,才会继续提供 Stable 更新。',
52
+ en: 'Switching back to Stable does not force an immediate downgrade. Stable updates resume once that channel catches up with or exceeds your current version.'
53
+ },
54
+ desktopUpdatesChannelStable: { zh: 'Stable', en: 'Stable' },
55
+ desktopUpdatesChannelBeta: { zh: 'Beta', en: 'Beta' },
56
+ desktopUpdatesBetaBadgeTitle: { zh: '当前正在跟随 Beta 通道', en: 'Following the Beta Channel' },
57
+ desktopUpdatesBetaBadgeDescription: {
58
+ zh: '你会更早收到新版本,但也可能遇到更多变动和回归。',
59
+ en: 'You will receive new versions earlier, but you may also encounter more change and regression risk.'
60
+ },
44
61
  desktopUpdatesAutomaticChecks: { zh: '自动检查更新', en: 'Automatic Update Checks' },
45
62
  desktopUpdatesAutomaticChecksHelp: {
46
63
  zh: '启动应用后自动检查是否有新版本。',
@@ -65,8 +82,17 @@ export const DESKTOP_UPDATE_LABELS: Record<string, { zh: string; en: string }> =
65
82
  desktopUpdatesDownloadFailed: { zh: '下载更新失败', en: 'Failed to download update' },
66
83
  desktopUpdatesApplyFailed: { zh: '应用更新失败', en: 'Failed to apply update' },
67
84
  desktopUpdatesPreferencesFailed: { zh: '保存更新偏好失败', en: 'Failed to save update preferences' },
85
+ desktopUpdatesChannelChangeFailed: { zh: '切换更新通道失败', en: 'Failed to change the release channel' },
68
86
  desktopUpdatesAlreadyLatest: { zh: '当前已经是最新版本。', en: 'You already have the latest version.' },
69
87
  desktopUpdatesReadyToApply: { zh: '更新已下载完成,可以在方便的时候重启应用。', en: 'The update is ready. Restart the app whenever you want to apply it.' },
70
88
  desktopUpdatesAvailable: { zh: '发现新版本 {version}。', en: 'Version {version} is available.' },
71
- desktopUpdatesUnknownVersion: { zh: '新版本', en: 'a new version' }
89
+ desktopUpdatesUnknownVersion: { zh: '新版本', en: 'a new version' },
90
+ desktopUpdatesChannelChanged: {
91
+ zh: '已切换到 {channel} 更新通道。',
92
+ en: 'Switched to the {channel} release channel.'
93
+ },
94
+ desktopUpdatesChannelChangedWithUpdate: {
95
+ zh: '已切换到 {channel} 通道,发现版本 {version}。',
96
+ en: 'Switched to the {channel} channel and found version {version}.'
97
+ }
72
98
  };
@@ -43,9 +43,20 @@ export const CHAT_LABELS: Record<string, { zh: string; en: string }> = {
43
43
  chatHistoryLoading: { zh: '加载会话历史中...', en: 'Loading session history...' },
44
44
  chatNoMessages: { zh: '暂无消息,发送一条开始对话。', en: 'No messages yet. Send one to start.' },
45
45
  chatBackToParent: { zh: '返回父会话', en: 'Back to parent' },
46
+ chatSessionOpenChildSessions: { zh: '查看子会话', en: 'View child sessions' },
46
47
  chatChildSessionLoading: { zh: '正在加载子会话…', en: 'Loading child session…' },
47
48
  chatChildSessionEmpty: { zh: '子会话还没有消息。', en: 'No child session messages yet.' },
48
- chatChildSessionClosePanel: { zh: '关闭子会话侧栏', en: 'Close child session panel' },
49
+ chatWorkspaceClosePanel: { zh: '关闭工作区侧栏', en: 'Close workspace panel' },
50
+ chatWorkspaceChildSessions: { zh: '子会话', en: 'Child sessions' },
51
+ chatWorkspaceOpenFiles: { zh: '打开的文件', en: 'Open files' },
52
+ chatWorkspacePreview: { zh: '预览', en: 'Preview' },
53
+ chatWorkspaceDiff: { zh: 'Diff', en: 'Diff' },
54
+ chatWorkspaceCloseFile: { zh: '关闭文件', en: 'Close file' },
55
+ chatWorkspaceLoadingFile: { zh: '正在加载文件…', en: 'Loading file…' },
56
+ chatWorkspacePreviewUnsupported: { zh: '该文件暂不支持在侧栏预览。', en: 'This file is not supported in the sidebar preview yet.' },
57
+ chatWorkspacePreviewEmpty: { zh: '当前没有可显示的文件内容。', en: 'No file content is available for preview.' },
58
+ chatWorkspaceDiffEmpty: { zh: '当前没有可显示的 diff 内容。', en: 'No diff content is available.' },
59
+ chatWorkspacePreviewTruncated: { zh: '内容已截断', en: 'Preview truncated' },
49
60
  chatSessionUnread: { zh: '会话有未读更新', en: 'Session has unread updates' },
50
61
  chatTyping: { zh: 'Agent 正在思考...', en: 'Agent is thinking...' },
51
62
  chatInputPlaceholder: { zh: '输入消息,输入 / 选择技能,Enter 发送,Shift + Enter 换行', en: 'Type a message, type / to select skills, Enter to send, Shift + Enter for newline' },
@@ -0,0 +1,62 @@
1
+ export const PWA_LABELS: Record<string, { zh: string; en: string }> = {
2
+ pwaInstallTitle: { zh: '安装为应用', en: 'Install as App' },
3
+ pwaInstallDescription: {
4
+ zh: '把当前 NextClaw UI 安装为独立入口,方便从桌面、启动器或任务栏直接打开。',
5
+ en: 'Install this NextClaw UI as a standalone entry point you can launch from your desktop, launcher, or dock.'
6
+ },
7
+ pwaInstallAction: { zh: '安装 NextClaw', en: 'Install NextClaw' },
8
+ pwaInstallDismiss: { zh: '不再提示', en: "Don't Ask Again" },
9
+ pwaInstallAccepted: { zh: '已打开安装面板。', en: 'Install prompt opened.' },
10
+ pwaInstalledToast: { zh: 'NextClaw 已安装为应用入口。', en: 'NextClaw is now installed as an app.' },
11
+ pwaInstallStatusAvailable: { zh: '可安装', en: 'Installable' },
12
+ pwaInstallStatusInstalled: { zh: '已安装', en: 'Installed' },
13
+ pwaInstallStatusDesktopHost: { zh: '桌面宿主已接管', en: 'Desktop Host Active' },
14
+ pwaInstallStatusUnavailable: { zh: '当前不可安装', en: 'Unavailable' },
15
+ pwaInstallCardPrompt: {
16
+ zh: '当前浏览器已经准备好安装面板。安装后,NextClaw 会以独立窗口形态打开,但仍沿用同一套 Web UI 和运行时连接逻辑。',
17
+ en: 'Your browser is ready to install NextClaw. Once installed, it opens in a standalone window while keeping the same Web UI and runtime behavior.'
18
+ },
19
+ pwaInstallCardManual: {
20
+ zh: '当前环境支持把 NextClaw 安装为应用,但浏览器没有提供即时安装弹窗。你仍可通过浏览器菜单中的“安装应用”或“添加到主屏幕”完成安装。',
21
+ en: 'This environment can install NextClaw as an app, but the browser did not expose an immediate install prompt. Use your browser menu to install or add it to the home screen.'
22
+ },
23
+ pwaInstallCardInstalled: {
24
+ zh: '当前已经以应用形态运行。浏览器访问与已安装形态共用同一套 NextClaw UI,不会分叉成第二套产品逻辑。',
25
+ en: 'NextClaw is already running as an installed app. Browser access and the installed experience share the same UI and product behavior.'
26
+ },
27
+ pwaInstallCardSuppressed: {
28
+ zh: '当前 UI 已运行在 Electron 桌面宿主中,原生桌面壳优先于 PWA 入口,因此这里不会继续展示安装入口。',
29
+ en: 'This UI is already running inside the Electron desktop host. The native desktop shell takes priority, so the PWA install entry stays hidden here.'
30
+ },
31
+ pwaInstallCardInsecureContext: {
32
+ zh: '当前地址不是浏览器允许安装 PWA 的安全上下文。请使用 `localhost`、`127.0.0.1` 或 HTTPS 域名访问。',
33
+ en: 'This address is not a secure context for browser-managed app installation. Use localhost, 127.0.0.1, or an HTTPS origin instead.'
34
+ },
35
+ pwaInstallCardDevServer: {
36
+ zh: '当前是 Vite 开发环境。为避免 service worker 缓存和 HMR 热更新互相干扰,开发态默认关闭 PWA 安装与更新能力;请使用 preview 或正式构建验证 PWA。',
37
+ en: 'This is the Vite development server. PWA install and update are disabled in dev to avoid service worker caching conflicts with HMR; use preview or a production build to verify the PWA.'
38
+ },
39
+ pwaInstallCardUnsupported: {
40
+ zh: '当前浏览器环境不支持这套安装能力,或缺少 PWA 所需的关键运行能力。',
41
+ en: 'This browser environment does not support the required installation capabilities for this PWA shell.'
42
+ },
43
+ pwaInstallPromptHint: {
44
+ zh: '安装后仍然连接当前本地或远端 NextClaw 服务,不会额外生成一套离线副本。',
45
+ en: 'The installed app still connects to the same local or remote NextClaw service instead of creating an offline copy.'
46
+ },
47
+ pwaInstallManualHint: {
48
+ zh: '如果浏览器没有弹出安装面板,请打开浏览器菜单,选择“安装应用”“安装此站点”或“添加到主屏幕”等同类入口。',
49
+ en: 'If the browser does not show an install prompt, open the browser menu and look for actions such as Install App, Install Site, or Add to Home Screen.'
50
+ },
51
+ pwaInstallBannerTitle: { zh: '把 NextClaw 固定成桌面入口', en: 'Pin NextClaw as an App' },
52
+ pwaInstallBannerDescription: {
53
+ zh: '当前站点已经满足安装条件。安装后你可以像打开普通应用一样直接进入 NextClaw。',
54
+ en: 'This site is ready to install. Once installed, you can launch NextClaw like a regular app.'
55
+ },
56
+ pwaUpdateBannerTitle: { zh: 'NextClaw 已准备好更新', en: 'NextClaw Update Ready' },
57
+ pwaUpdateBannerDescription: {
58
+ zh: '检测到新的 PWA 壳版本,刷新后即可切换到最新 UI 资源。',
59
+ en: 'A newer PWA shell version is ready. Refresh to switch to the latest UI assets.'
60
+ },
61
+ pwaUpdateAction: { zh: '刷新更新', en: 'Refresh Now' }
62
+ };
@@ -0,0 +1,120 @@
1
+ export const RUNTIME_CONTROL_LABELS: Record<string, { zh: string; en: string }> = {
2
+ runtimePageTitle: { zh: '路由与运行时', en: 'Routing & Runtime' },
3
+ runtimePageDescription: {
4
+ zh: '对齐 OpenClaw 的多 Agent 路由:绑定规则、Agent 池、私聊范围。',
5
+ en: 'Align multi-agent routing with OpenClaw: bindings, agent pool, and DM scope.'
6
+ },
7
+ runtimeLoading: { zh: '加载运行时配置中...', en: 'Loading runtime settings...' },
8
+ runtimeControlTitle: { zh: '服务管理', en: 'Service Management' },
9
+ runtimeControlDescription: {
10
+ zh: '明确当前服务状态,并在宿主允许的范围内执行启动、重启、停止或桌面应用重启。',
11
+ en: 'Make the current service state explicit and manage start, restart, stop, or full desktop app restart when the host allows it.'
12
+ },
13
+ runtimePresenceTitle: { zh: '运行形态与常驻', en: 'Presence & Lifecycle' },
14
+ runtimePresenceDescription: {
15
+ zh: '明确当前环境里“关闭窗口/页面”会发生什么,以及哪些后台能力由宿主层负责。',
16
+ en: 'Make the current environment explicit: what happens when the window or page closes, and which background behaviors belong to the host.'
17
+ },
18
+ runtimePresenceLoading: { zh: '正在读取常驻状态...', en: 'Loading presence settings...' },
19
+ runtimePresenceLoadFailed: { zh: '读取桌面端常驻状态失败', en: 'Failed to load desktop presence settings' },
20
+ runtimePresenceBehaviorLabel: { zh: '当前关闭行为', en: 'Current Close Behavior' },
21
+ runtimePresenceBehaviorBackground: { zh: '关闭窗口时隐藏到后台', en: 'Closing the window hides it to the background' },
22
+ runtimePresenceBehaviorQuit: { zh: '关闭窗口时退出应用', en: 'Closing the window quits the app' },
23
+ runtimePresenceCloseToBackground: { zh: '关闭窗口时继续在后台运行', en: 'Keep running in background when closing the window' },
24
+ runtimePresenceCloseToBackgroundHelp: {
25
+ zh: '开启后,关闭桌面窗口只会隐藏到托盘,不会停止本地 NextClaw 服务。',
26
+ en: 'When enabled, closing the desktop window hides it to the tray instead of stopping the local NextClaw service.'
27
+ },
28
+ runtimePresenceLaunchAtLogin: { zh: '登录系统时自动启动 NextClaw', en: 'Launch NextClaw at login' },
29
+ runtimePresenceLaunchAtLoginHelp: {
30
+ zh: '开启后,桌面端会在登录系统后自动启动,并默认保持后台运行。',
31
+ en: 'When enabled, the desktop app starts automatically after login and stays in the background by default.'
32
+ },
33
+ runtimePresenceLaunchAtLoginUnavailable: {
34
+ zh: '当前环境暂不支持在这里配置开机自启。',
35
+ en: 'Launch-at-login is not configurable in this environment yet.'
36
+ },
37
+ runtimePresenceSaved: { zh: '桌面端常驻设置已保存', en: 'Desktop presence settings saved' },
38
+ runtimePresenceSaveFailed: { zh: '保存桌面端常驻设置失败', en: 'Failed to save desktop presence settings' },
39
+ runtimePresenceManagedLocalTitle: { zh: '浏览器只是本地服务控制面', en: 'The browser is only a control surface for the local service' },
40
+ runtimePresenceManagedLocalDescription: {
41
+ zh: '关闭浏览器标签页不会停止本地 NextClaw 服务。服务生命周期由本地受管服务负责,而不是由页面生命周期决定。',
42
+ en: 'Closing the browser tab does not stop the local NextClaw service. The local managed service owns the lifecycle, not the page.'
43
+ },
44
+ runtimePresenceSelfHostedTitle: { zh: '页面不拥有自托管服务生命周期', en: 'The page does not own the self-hosted service lifecycle' },
45
+ runtimePresenceSelfHostedDescription: {
46
+ zh: '关闭浏览器页面不会影响自托管实例。服务启停和开机常驻应由宿主环境或部署层管理。',
47
+ en: 'Closing the browser page does not affect the self-hosted instance. Service start, stop, and auto-start belong to the host environment or deployment layer.'
48
+ },
49
+ runtimePresenceSharedTitle: { zh: '共享网页端只暴露状态,不暴露进程控制', en: 'Shared web exposes service state, not process ownership' },
50
+ runtimePresenceSharedDescription: {
51
+ zh: '关闭页面只会结束当前连接或会话,不会影响共享服务本体。服务生命周期由平台托管层负责。',
52
+ en: 'Closing the page only ends the current connection or session, not the shared service itself. The platform owns the lifecycle.'
53
+ },
54
+ runtimeControlLoading: { zh: '读取运行时控制能力中...', en: 'Loading runtime control capabilities...' },
55
+ runtimeControlLoadFailed: { zh: '读取运行时控制状态失败', en: 'Failed to load runtime control state' },
56
+ runtimeControlServiceRunning: { zh: '服务运行中', en: 'Service running' },
57
+ runtimeControlServiceStopped: { zh: '服务已停止', en: 'Service stopped' },
58
+ runtimeControlServiceStarting: { zh: '正在启动服务', en: 'Starting service' },
59
+ runtimeControlServiceStopping: { zh: '正在停止服务', en: 'Stopping service' },
60
+ runtimeControlServiceRestarting: { zh: '正在重启服务', en: 'Restarting service' },
61
+ runtimeControlServiceUnknown: { zh: '服务状态未知', en: 'Service state unknown' },
62
+ runtimeControlHealthy: { zh: '运行时正常', en: 'Runtime healthy' },
63
+ runtimeControlStartingService: { zh: '正在启动服务', en: 'Starting service' },
64
+ runtimeControlRestartingService: { zh: '正在重启服务', en: 'Restarting service' },
65
+ runtimeControlStoppingService: { zh: '正在停止服务', en: 'Stopping service' },
66
+ runtimeControlRestartingApp: { zh: '正在重启应用', en: 'Restarting app' },
67
+ runtimeControlRecovering: { zh: '正在恢复连接', en: 'Recovering connection' },
68
+ runtimeControlFailed: { zh: '重启失败', en: 'Restart failed' },
69
+ runtimeControlUnavailable: { zh: '当前不可用', en: 'Currently unavailable' },
70
+ runtimeControlEnvironmentDesktop: { zh: '桌面端内嵌运行时', en: 'Desktop embedded runtime' },
71
+ runtimeControlEnvironmentManagedService: { zh: '本地托管服务', en: 'Managed local service' },
72
+ runtimeControlEnvironmentSelfHosted: { zh: '自托管网页端', en: 'Self-hosted web' },
73
+ runtimeControlEnvironmentSharedWeb: { zh: '共享网页端', en: 'Shared web' },
74
+ runtimeControlStartService: { zh: '启动服务', en: 'Start Service' },
75
+ runtimeControlRestartService: { zh: '重启服务', en: 'Restart Service' },
76
+ runtimeControlStopService: { zh: '停止服务', en: 'Stop Service' },
77
+ runtimeControlRestartApp: { zh: '重启应用', en: 'Restart App' },
78
+ runtimeControlStartingServiceHelp: {
79
+ zh: '正在启动 NextClaw 服务,页面可能会在服务恢复后重新连接。',
80
+ en: 'Starting the NextClaw service. The page may reconnect after the service becomes available.'
81
+ },
82
+ runtimeControlRestartingServiceHelp: {
83
+ zh: '正在重启 NextClaw 服务,页面可能会短暂断开。',
84
+ en: 'Restarting the NextClaw service. The page may disconnect briefly.'
85
+ },
86
+ runtimeControlStoppingServiceHelp: {
87
+ zh: '正在停止 NextClaw 服务,当前页面会很快断开。',
88
+ en: 'Stopping the NextClaw service. This page will disconnect shortly.'
89
+ },
90
+ runtimeControlRestartingAppHelp: {
91
+ zh: '正在重新启动桌面应用,当前窗口会短暂关闭并立即拉起。',
92
+ en: 'Restarting the desktop app. The current window will close briefly and relaunch.'
93
+ },
94
+ runtimeControlRecoveringHelp: {
95
+ zh: '正在等待服务恢复连接...',
96
+ en: 'Waiting for the service to come back...'
97
+ },
98
+ runtimeControlRecovered: { zh: '运行时已恢复连接', en: 'Runtime connection restored' },
99
+ runtimeControlActionFailed: { zh: '服务管理动作失败', en: 'Service management action failed' },
100
+ runtimeControlRestartAppConfirm: {
101
+ zh: '这会重启整个 NextClaw 桌面应用,并中断当前窗口。确定继续吗?',
102
+ en: 'This restarts the entire NextClaw desktop app and interrupts the current window. Continue?'
103
+ },
104
+ runtimeControlStopServiceConfirm: {
105
+ zh: '这会停止当前 NextClaw 服务,当前页面会立即断开。确定继续吗?',
106
+ en: 'This stops the current NextClaw service and disconnects the page immediately. Continue?'
107
+ },
108
+ runtimeRestartAppUnavailable: {
109
+ zh: '当前环境不支持从前端重启整个应用。',
110
+ en: 'This environment does not support restarting the entire app from the UI.'
111
+ },
112
+ runtimeControlDesktopServiceHint: {
113
+ zh: '桌面端由 Launcher 持有应用生命周期;普通用户无需单独停止内嵌服务。',
114
+ en: 'The desktop launcher owns the app lifecycle, so end users do not stop the embedded service separately.'
115
+ },
116
+ runtimeRecoveryTimedOut: {
117
+ zh: '等待运行时恢复超时,请稍后重试或查看日志。',
118
+ en: 'Timed out waiting for the runtime to recover. Try again or inspect the logs.'
119
+ }
120
+ };
package/src/lib/i18n.ts CHANGED
@@ -15,7 +15,9 @@ import {
15
15
  import { DESKTOP_UPDATE_LABELS } from './desktop-update-labels.utils';
16
16
  import { MARKETPLACE_LABELS } from './i18n.marketplace';
17
17
  import { PATH_PICKER_LABELS } from './i18n-runtime/i18n.path-picker';
18
+ import { PWA_LABELS } from './i18n.pwa';
18
19
  import { REMOTE_LABELS } from './i18n.remote';
20
+ import { RUNTIME_CONTROL_LABELS } from './i18n.runtime-control';
19
21
  import { SEARCH_LABELS } from './i18n.search';
20
22
  export type { I18nLanguage };
21
23
  export { getLanguage, getLocale, initializeI18n, LANGUAGE_OPTIONS, resolveInitialLanguage, setLanguage, subscribeLanguageChange };
@@ -23,7 +25,6 @@ export function formatDateTime(value?: string | Date, lang: I18nLanguage = getLa
23
25
  if (!value) {
24
26
  return '-';
25
27
  }
26
-
27
28
  const date = value instanceof Date ? value : new Date(value);
28
29
  if (Number.isNaN(date.getTime())) {
29
30
  return typeof value === 'string' ? value : '-';
@@ -301,10 +302,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
301
302
  authRetryStatus: { zh: '重试', en: 'Retry' },
302
303
  authStatusLoadFailed: { zh: '无法获取认证状态,请检查 UI 服务是否正常。', en: 'Failed to load authentication status. Check whether the UI server is healthy.' },
303
304
 
304
- // Runtime
305
- runtimePageTitle: { zh: '路由与运行时', en: 'Routing & Runtime' },
306
- runtimePageDescription: { zh: '对齐 OpenClaw 的多 Agent 路由:绑定规则、Agent 池、私聊范围。', en: 'Align multi-agent routing with OpenClaw: bindings, agent pool, and DM scope.' },
307
- runtimeLoading: { zh: '加载运行时配置中...', en: 'Loading runtime settings...' },
305
+ ...RUNTIME_CONTROL_LABELS,
308
306
  authSecurityTitle: { zh: 'Security', en: 'Security' },
309
307
  authSecurityDescription: {
310
308
  zh: '保持本机控制台默认简单;只有在你需要远程暴露时,再给 UI 加一层登录门。',
@@ -519,7 +517,6 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
519
517
  enterTag: { zh: '输入后按回车...', en: 'Type and press Enter...' },
520
518
  headerName: { zh: 'Header 名称', en: 'Header Name' },
521
519
  headerValue: { zh: 'Header 值', en: 'Header Value' },
522
-
523
520
  // Doc Browser
524
521
  docBrowserTitle: { zh: '内嵌浏览器', en: 'Embedded Browser' },
525
522
  docBrowserSearchPlaceholder: { zh: '搜索,也可以输入文档地址直接打开', en: 'Search, or enter a doc URL to open' },
@@ -533,6 +530,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
533
530
  docBrowserCloseTab: { zh: '关闭标签', en: 'Close Tab' },
534
531
  docBrowserTabUntitled: { zh: '未命名', en: 'Untitled' },
535
532
  ...PATH_PICKER_LABELS,
533
+ ...PWA_LABELS,
536
534
  ...CHANNEL_AUTH_LABELS,
537
535
  };
538
536
 
package/src/main.tsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
3
  import { BrowserRouter } from 'react-router-dom';
4
- import App from './App';
4
+ import App from './app';
5
5
  import { I18nProvider } from '@/components/providers/I18nProvider';
6
6
  import { ThemeProvider } from '@/components/providers/ThemeProvider';
7
7
  import './index.css';