@wu529778790/open-im 1.9.3-beta.8 → 1.9.3

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.
@@ -8,13 +8,14 @@ export const PAGE_TEXTS = {
8
8
  heroKicker: "Local AI bridge",
9
9
  langButton: "\u4e2d\u6587",
10
10
  darkModeToggle: "Toggle dark mode",
11
+ headerToolbarAria: "Bridge: validate, save, start, stop",
11
12
  controlCenter: "Control center",
12
13
  sidebarNoteTitle: "Local workflow",
13
14
  sidebarNoteBody: "Configure at least one platform, save the config, then start the bridge from Service.",
14
15
  mode: "Flow",
15
16
  dashboardTitle: "Dashboard",
16
17
  dashboardSubtitle: "Platform health status",
17
- dashboardSubtitleFull: "Platform status and setup progress",
18
+ dashboardSubtitleFull: "Platform and service status",
18
19
  quickActionsTitle: "Quick Actions",
19
20
  serviceTitle: "Service Control",
20
21
  serviceHint: "Validate, save, start, and stop the local bridge from one place.",
@@ -40,7 +41,13 @@ export const PAGE_TEXTS = {
40
41
  serviceIdleMeta: "Bridge has not been started yet",
41
42
  listSeparator: ", ",
42
43
  platformsTitle: "Platforms",
43
- platformsHint: "Follow the setup checklist on Overview first. Disabled platforms still keep saved values.",
44
+ navConfigFiles: "Config files",
45
+ configFilesTitle: "Config files (JSON)",
46
+ configFilesHint: "Edit the files on disk directly. Each card has its own Save. Platform / AI forms and Service → Save config still apply.",
47
+ openImConfigCardHint: "Full open-im configuration. Use Format, then Save. Invalid JSON is rejected.",
48
+ claudeSettingsCardHint: "Claude SDK environment (ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, etc.). Set API access here without shell exports.",
49
+ claudeJsonShortcutHint: "Edit ~/.claude/settings.json in the sidebar «Config files» section below.",
50
+ platformsHint: "Disabled platforms keep their saved values.",
44
51
  enabled: "Enabled",
45
52
  readyState: "Ready",
46
53
  setupRequired: "Setup required",
@@ -81,7 +88,7 @@ export const PAGE_TEXTS = {
81
88
  optional: "Optional",
82
89
  commaSeparatedIds: "Comma-separated IDs",
83
90
  aiTitle: "AI Tooling",
84
- aiHint: "Pick the default tool first. Claude SDK uses API keys in ~/.claude/settings.json (editable below). Codex / CodeBuddy need a CLI path.",
91
+ aiHint: "Pick the default tool first. Claude SDK keys: sidebar Config files ~/.claude/settings.json. Codex / CodeBuddy need a CLI path here.",
85
92
  claudeNote: "Claude credentials are still read from environment variables or ~/.claude/settings.json. This page manages local bridge config, not Claude account auth.",
86
93
  aiCommonTitle: "Shared defaults",
87
94
  aiCommonHint: "Choose the default AI tool first. Tool-specific fields are shown below.",
@@ -94,14 +101,11 @@ export const PAGE_TEXTS = {
94
101
  codexCli: "Codex CLI path",
95
102
  codebuddyCli: "CodeBuddy CLI path",
96
103
  codexProxy: "Codex proxy",
97
- claudeTimeout: "Claude timeout (ms)",
98
104
  claudeConfigPath: "Config file location",
99
105
  claudeAuthToken: "ANTHROPIC_AUTH_TOKEN",
100
106
  claudeBaseUrl: "ANTHROPIC_BASE_URL",
101
107
  claudeModel: "ANTHROPIC_MODEL",
102
108
  claudeProxy: "Proxy (optional)",
103
- codexTimeout: "Codex timeout (ms)",
104
- codebuddyTimeout: "CodeBuddy timeout (ms)",
105
109
  hookPort: "Hook port",
106
110
  logLevel: "Log level",
107
111
  logLevelDefault: "default (app default)",
@@ -134,28 +138,10 @@ export const PAGE_TEXTS = {
134
138
  saveBtn: "Save",
135
139
  jsonValid: "Valid JSON",
136
140
  jsonInvalid: "Invalid JSON: {error}",
137
- wizardTitle: "First-time setup",
138
- wizardStep1Title: "Connect an IM channel",
139
- wizardStep1Desc: "Enable at least one platform below and fill every required field. Use Check config when available.",
140
- wizardStep2Title: "Set the default AI tool",
141
- wizardStep2Desc: "Claude SDK: API keys live in ~/.claude/settings.json (expand on this page). Codex / CodeBuddy: set the CLI path in the AI section.",
142
- wizardStep3Title: "Validate and save",
143
- wizardStep3Desc: "Click Validate, then Save config. Fix any errors shown in the banner above.",
144
- wizardStep4Title: "Start the bridge",
145
- wizardStep4Desc: "Open Service and click Start bridge. If it exits, read ~/.open-im/logs.",
146
- wizardStatusDone: "Done",
147
- wizardStatusTodo: "To do",
148
- wizardJumpPlatforms: "Open Platforms",
149
- wizardJumpAi: "Open AI",
150
- wizardJumpService: "Open Service",
151
141
  validationNoPlatformEnabled: "Enable at least one IM platform and fill its required credentials before saving.",
152
142
  validationPlatformIncomplete: "Platform \"{platform}\" is enabled but these required fields are empty: {fields}",
153
143
  validationAiCodexNoCli: "Default AI is Codex but Codex CLI path is empty. Set it under AI Tooling or change the default tool.",
154
144
  validationAiCodebuddyNoCli: "Default AI is CodeBuddy but CodeBuddy CLI path is empty. Set it under AI Tooling or change the default tool.",
155
- onboardingTitle: "Welcome to open-im",
156
- onboardingDismiss: "Got it",
157
- onboardingReadme: "README (setup)",
158
- onboardingBody: "<p><strong>open-im</strong> bridges Telegram, Feishu, QQ, WeWork, DingTalk, and WorkBuddy to Claude / Codex / CodeBuddy on your machine.</p><ol style=\"margin:12px 0 12px 18px;line-height:1.65\"><li>Pick <strong>one</strong> chat app in <strong>Platforms</strong> and paste credentials (see hints under each field).</li><li>Set the <strong>default AI</strong> and, for Claude SDK, API keys in <strong>~/.claude/settings.json</strong> on this page.</li><li>Use <strong>Validate</strong> then <strong>Save config</strong>, then <strong>Start bridge</strong> under Service.</li><li>CLI alternative: run <code style=\"background:var(--bg-tertiary);padding:2px 6px;border-radius:4px\">open-im init</code> in a terminal.</li></ol>",
159
145
  tipTelegramToken: 'From Telegram, open <a href="https://t.me/BotFather" target="_blank" rel="noopener">@BotFather</a> → /newbot → copy the token.',
160
146
  tipFeishuAppId: "Feishu Open Platform → your app → Credentials → App ID.",
161
147
  tipFeishuSecret: "Same page → App Secret (click show / reset if needed).",
@@ -174,13 +160,14 @@ export const PAGE_TEXTS = {
174
160
  heroKicker: "\u672c\u5730 AI \u6865\u63a5",
175
161
  langButton: "EN",
176
162
  darkModeToggle: "\u5207\u6362\u6697\u9ed1\u6a21\u5f0f",
163
+ headerToolbarAria: "\u6865\u63a5\uff1a\u6821\u9a8c\u3001\u4fdd\u5b58\u3001\u542f\u52a8\u3001\u505c\u6b62",
177
164
  controlCenter: "\u63a7\u5236\u4e2d\u5fc3",
178
165
  sidebarNoteTitle: "\u672c\u5730\u5de5\u4f5c\u6d41",
179
166
  sidebarNoteBody: "\u81f3\u5c11\u914d\u7f6e\u4e00\u4e2a\u5e73\u53f0\uff0c\u4fdd\u5b58\u914d\u7f6e\uff0c\u7136\u540e\u5728\u670d\u52a1\u63a7\u5236\u533a\u542f\u52a8\u6865\u63a5\u3002",
180
167
  mode: "\u6a21\u5f0f",
181
168
  dashboardTitle: "\u6982\u89c8",
182
169
  dashboardSubtitle: "\u5e73\u53f0\u5065\u5eb7\u72b6\u6001",
183
- dashboardSubtitleFull: "\u5e73\u53f0\u72b6\u6001\u4e0e\u63a5\u5165\u8fdb\u5ea6",
170
+ dashboardSubtitleFull: "\u5e73\u53f0\u4e0e\u670d\u52a1\u72b6\u6001",
184
171
  quickActionsTitle: "\u5feb\u6377\u64cd\u4f5c",
185
172
  refreshHealth: "\u5237\u65b0\u5065\u5eb7\u72b6\u6001",
186
173
  viewConfig: "\u67e5\u770b\u914d\u7f6e",
@@ -204,7 +191,13 @@ export const PAGE_TEXTS = {
204
191
  serviceIdleMeta: "\u6865\u63a5\u670d\u52a1\u5c1a\u672a\u542f\u52a8",
205
192
  listSeparator: "\u3001",
206
193
  platformsTitle: "\u5e73\u53f0\u914d\u7f6e",
207
- platformsHint: "\u5efa\u8bae\u5148\u5728\u6982\u89c8\u9875\u6309\u5f15\u5bfc\u6b65\u9aa4\u64cd\u4f5c\u3002\u7981\u7528\u7684\u5e73\u53f0\u4ecd\u4fdd\u7559\u5df2\u4fdd\u5b58\u7684\u503c\u3002",
194
+ navConfigFiles: "\u914d\u7f6e\u6587\u4ef6",
195
+ configFilesTitle: "\u914d\u7f6e\u6587\u4ef6\uff08JSON\uff09",
196
+ configFilesHint: "\u76f4\u63a5\u7f16\u8f91\u78c1\u76d8\u4e0a\u7684\u6587\u4ef6\uff0c\u6bcf\u5f20\u5361\u7247\u6709\u72ec\u7acb\u4fdd\u5b58\u6309\u94ae\u3002\u5e73\u53f0 / AI \u8868\u5355\u4e0e\u670d\u52a1\u533a\u7684\u300c\u4fdd\u5b58\u914d\u7f6e\u300d\u4ecd\u7136\u6709\u6548\u3002",
197
+ openImConfigCardHint: "open-im \u5b8c\u6574\u914d\u7f6e\u3002\u5148\u683c\u5f0f\u5316\u518d\u4fdd\u5b58\uff1bJSON \u4e0d\u5408\u6cd5\u65f6\u65e0\u6cd5\u5199\u5165\u3002",
198
+ claudeSettingsCardHint: "Claude SDK \u73af\u5883\u53d8\u91cf\uff08ANTHROPIC_API_KEY\u3001ANTHROPIC_BASE_URL\u3001ANTHROPIC_MODEL \u7b49\uff09\u3002\u5728\u6b64\u914d\u7f6e API\uff0c\u65e0\u9700\u5728\u7ec8\u7aef export\u3002",
199
+ claudeJsonShortcutHint: "\u63d0\u793a\uff1a\u8bf7\u5728\u5de6\u4fa7\u680f\u300c\u914d\u7f6e\u6587\u4ef6\u300d\u5206\u533a\u7f16\u8f91 ~/.claude/settings.json\u3002",
200
+ platformsHint: "\u7981\u7528\u7684\u5e73\u53f0\u4ecd\u4fdd\u7559\u5df2\u4fdd\u5b58\u7684\u503c\u3002",
208
201
  enabled: "\u542f\u7528",
209
202
  readyState: "\u5df2\u5c31\u7eea",
210
203
  setupRequired: "\u9700\u8981\u5b8c\u6210\u914d\u7f6e",
@@ -244,7 +237,7 @@ export const PAGE_TEXTS = {
244
237
  optional: "\u53ef\u9009",
245
238
  commaSeparatedIds: "\u591a\u4e2a ID \u7528\u9017\u53f7\u5206\u9694",
246
239
  aiTitle: "AI \u5de5\u5177\u914d\u7f6e",
247
- aiHint: "\u5148\u9009\u9ed8\u8ba4 AI\u3002Claude SDK \u7684 API Key \u5199\u5728 ~/.claude/settings.json\uff08\u4e0b\u65b9\u53ef\u7f16\u8f91\uff09\u3002Codex / CodeBuddy \u9700\u586b CLI \u8def\u5f84\u3002",
240
+ aiHint: "\u5148\u9009\u9ed8\u8ba4 AI\u3002Claude SDK\uff1a\u5de6\u4fa7\u680f\u300c\u914d\u7f6e\u6587\u4ef6\u300d\u2192 ~/.claude/settings.json\u3002Codex / CodeBuddy \u5728\u6b64\u586b CLI \u8def\u5f84\u3002",
248
241
  claudeNote: "Claude \u51ed\u8bc1\u4ecd\u7136\u4ece\u73af\u5883\u53d8\u91cf\u6216 ~/.claude/settings.json \u8bfb\u53d6\u3002\u8fd9\u4e2a\u9875\u9762\u53ea\u7ba1\u7406\u672c\u5730\u6865\u63a5\u914d\u7f6e\uff0c\u4e0d\u8d1f\u8d23 Claude \u8d26\u53f7\u767b\u5f55\u3002",
249
242
  aiCommonTitle: "\u516c\u5171\u9ed8\u8ba4\u914d\u7f6e",
250
243
  aiCommonHint: "\u5148\u9009\u62e9\u9ed8\u8ba4 AI \u5de5\u5177\uff0c\u4e0b\u65b9\u518d\u663e\u793a\u5bf9\u5e94\u7684\u5de5\u5177\u4e13\u5c5e\u914d\u7f6e\u3002",
@@ -257,11 +250,8 @@ export const PAGE_TEXTS = {
257
250
  codexCli: "Codex CLI \u8def\u5f84",
258
251
  codebuddyCli: "CodeBuddy CLI \u8def\u5f84",
259
252
  codexProxy: "Codex \u4ee3\u7406",
260
- claudeTimeout: "Claude \u8d85\u65f6\uff08\u6beb\u79d2\uff09",
261
253
  claudeConfigPath: "\u914d\u7f6e\u6587\u4ef6\u4f4d\u7f6e",
262
254
  claudeProxy: "\u4ee3\u7406\uff08\u53ef\u9009\uff09",
263
- codexTimeout: "Codex \u8d85\u65f6\uff08\u6beb\u79d2\uff09",
264
- codebuddyTimeout: "CodeBuddy \u8d85\u65f6\uff08\u6beb\u79d2\uff09",
265
255
  hookPort: "Hook \u7aef\u53e3",
266
256
  logLevel: "\u65e5\u5fd7\u7ea7\u522b",
267
257
  logLevelDefault: "default\uff08\u7a0b\u5e8f\u9ed8\u8ba4\uff09",
@@ -294,28 +284,10 @@ export const PAGE_TEXTS = {
294
284
  saveBtn: "\u4fdd\u5b58",
295
285
  jsonValid: "JSON \u6709\u6548",
296
286
  jsonInvalid: "JSON \u65e0\u6548\uff1a{error}",
297
- wizardTitle: "\u9996\u6b21\u4f7f\u7528\u5f15\u5bfc",
298
- wizardStep1Title: "\u63a5\u5165\u4e00\u4e2a IM \u6e20\u9053",
299
- wizardStep1Desc: "\u5728\u4e0b\u65b9\u542f\u7528\u81f3\u5c11\u4e00\u4e2a\u5e73\u53f0\uff0c\u5e76\u586b\u5199\u6240\u6709\u5fc5\u586b\u9879\u3002\u6709\u6761\u4ef6\u65f6\u7528\u300c\u6821\u9a8c\u914d\u7f6e\u300d\u6d4b\u8bd5\u51ed\u8bc1\u3002",
300
- wizardStep2Title: "\u8bbe\u7f6e\u9ed8\u8ba4 AI \u5de5\u5177",
301
- wizardStep2Desc: "Claude SDK\uff1aAPI Key \u5199\u5728 ~/.claude/settings.json\uff08\u672c\u9875\u53ef\u5c55\u5f00\u7f16\u8f91\uff09\u3002Codex / CodeBuddy\uff1a\u5728 AI \u533a\u586b CLI \u8def\u5f84\u3002",
302
- wizardStep3Title: "\u6821\u9a8c\u5e76\u4fdd\u5b58",
303
- wizardStep3Desc: "\u5148\u70b9\u300c\u6821\u9a8c\u300d\uff0c\u518d\u70b9\u300c\u4fdd\u5b58\u914d\u7f6e\u300d\u3002\u6309\u9876\u90e8\u63d0\u793a\u4fee\u590d\u9519\u8bef\u3002",
304
- wizardStep4Title: "\u542f\u52a8\u6865\u63a5",
305
- wizardStep4Desc: "\u5230\u300c\u670d\u52a1\u300d\u533a\u70b9\u300c\u542f\u52a8\u6865\u63a5\u300d\u3002\u82e5\u9a6c\u4e0a\u9000\u51fa\uff0c\u67e5 ~/.open-im/logs \u65e5\u5fd7\u3002",
306
- wizardStatusDone: "\u5df2\u5b8c\u6210",
307
- wizardStatusTodo: "\u5f85\u5b8c\u6210",
308
- wizardJumpPlatforms: "\u6253\u5f00\u5e73\u53f0\u914d\u7f6e",
309
- wizardJumpAi: "\u6253\u5f00 AI \u914d\u7f6e",
310
- wizardJumpService: "\u6253\u5f00\u670d\u52a1\u63a7\u5236",
311
287
  validationNoPlatformEnabled: "\u4fdd\u5b58\u524d\u8bf7\u81f3\u5c11\u542f\u7528\u4e00\u4e2a IM \u5e73\u53f0\uff0c\u5e76\u586b\u5b8c\u5fc5\u586b\u51ed\u8bc1\u3002",
312
288
  validationPlatformIncomplete: "\u5e73\u53f0\u300c{platform}\u300d\u5df2\u542f\u7528\uff0c\u4f46\u4ee5\u4e0b\u5fc5\u586b\u9879\u4e3a\u7a7a\uff1a{fields}",
313
289
  validationAiCodexNoCli: "\u9ed8\u8ba4 AI \u4e3a Codex\uff0c\u4f46 Codex CLI \u8def\u5f84\u4e3a\u7a7a\u3002\u8bf7\u5728 AI \u533a\u586b\u5199\uff0c\u6216\u6539\u9ed8\u8ba4\u5de5\u5177\u3002",
314
290
  validationAiCodebuddyNoCli: "\u9ed8\u8ba4 AI \u4e3a CodeBuddy\uff0c\u4f46 CodeBuddy CLI \u8def\u5f84\u4e3a\u7a7a\u3002\u8bf7\u5728 AI \u533a\u586b\u5199\uff0c\u6216\u6539\u9ed8\u8ba4\u5de5\u5177\u3002",
315
- onboardingTitle: "\u6b22\u8fce\u4f7f\u7528 open-im",
316
- onboardingDismiss: "\u77e5\u9053\u4e86",
317
- onboardingReadme: "README\uff08\u90e8\u7f72\u8bf4\u660e\uff09",
318
- onboardingBody: "<p><strong>open-im</strong> \u5728\u672c\u673a\u628a Telegram\u3001\u98de\u4e66\u3001QQ\u3001\u4f01\u5fae\u3001\u9489\u9489\u3001WorkBuddy \u7b49\u6e20\u9053\u8fde\u5230 Claude / Codex / CodeBuddy\u3002</p><ol style=\"margin:12px 0 12px 18px;line-height:1.65\"><li>\u5728<strong>\u5e73\u53f0\u914d\u7f6e</strong>\u9009\u4e00\u4e2a\u804a\u5929\u5e94\u7528\uff0c\u6309\u5b57\u6bb5\u4e0b\u65b9\u63d0\u793a\u586b\u51ed\u8bc1\u3002</li><li>\u8bbe<strong>\u9ed8\u8ba4 AI</strong>\uff1b\u7528 Claude SDK \u65f6\u628a API Key \u5199\u8fdb<strong>~/.claude/settings.json</strong>\uff08\u672c\u9875\u53ef\u7f16\u8f91\uff09\u3002</li><li>\u5148<strong>\u6821\u9a8c</strong>\u518d<strong>\u4fdd\u5b58\u914d\u7f6e</strong>\uff0c\u7136\u540e\u5230<strong>\u670d\u52a1</strong>\u542f\u52a8\u6865\u63a5\u3002</li><li>\u4e5f\u53ef\u5728\u7ec8\u7aef\u8fd0\u884c <code style=\"background:var(--bg-tertiary);padding:2px 6px;border-radius:4px\">open-im init</code> \u4ea4\u4e92\u914d\u7f6e\u3002</li></ol>",
319
291
  tipTelegramToken: '\u5728 Telegram \u641c <a href="https://t.me/BotFather" target="_blank" rel="noopener">@BotFather</a>\uff0c\u53d1 /newbot \u521b\u5efa\u673a\u5668\u4eba\u540e\u590d\u5236 Token\u3002',
320
292
  tipFeishuAppId: "\u98de\u4e66\u5f00\u653e\u5e73\u53f0 \u2192 \u5e94\u7528 \u2192 \u51ed\u8bc1\u4e0e\u57fa\u7840\u4fe1\u606f \u2192 App ID\u3002",
321
293
  tipFeishuSecret: "\u540c\u4e00\u9875 App Secret\uff08\u53ef\u91cd\u7f6e\u540e\u67e5\u770b\uff09\u3002",
@@ -10,8 +10,6 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
10
10
  const aiTools = ["claude", "codex", "codebuddy"];
11
11
  const STORAGE_KEY_LANG = "open-im-web-lang";
12
12
  const STORAGE_KEY_DARK_MODE = "open-im-web-dark-mode";
13
- const STORAGE_KEY_ONBOARDING = "open-im-dashboard-onboarding-v1";
14
- const STORAGE_KEY_SAVED_SESSION = "open-im-setup-saved-session";
15
13
  const POLLING_INTERVAL = 10000;
16
14
  const toolLabels = { claude: "Claude", codex: "Codex", codebuddy: "CodeBuddy" };
17
15
 
@@ -107,7 +105,17 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
107
105
 
108
106
  // Button state
109
107
  const setBusy = (busy) => {
110
- ["validateButton","saveButton","startButton","stopButton","langButton"].forEach((id) => {
108
+ [
109
+ "validateButton",
110
+ "saveButton",
111
+ "startButton",
112
+ "stopButton",
113
+ "headerValidateButton",
114
+ "headerSaveButton",
115
+ "headerStartButton",
116
+ "headerStopButton",
117
+ "langButton",
118
+ ].forEach((id) => {
111
119
  const node = el(id);
112
120
  if (node) node.disabled = busy;
113
121
  });
@@ -146,9 +154,9 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
146
154
  const LANGUAGE_UPDATES = {
147
155
  simpleText: [
148
156
  { id: "mainTitle", key: "dashboardTitle" },
149
- { id: "mainSubtitle", key: "dashboardSubtitleFull" },
150
157
  { id: "navOverviewText", key: "dashboardTitle" },
151
158
  { id: "navPlatformsText", key: "platformsTitle" },
159
+ { id: "navConfigFilesText", key: "navConfigFiles" },
152
160
  { id: "navAiText", key: "aiTitle" },
153
161
  { id: "navServiceText", key: "serviceTitle" },
154
162
  { id: "footerGithubText", value: "GitHub" },
@@ -163,28 +171,17 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
163
171
  { id: "statConfiguredLabel", key: "statConfiguredLabel" },
164
172
  { id: "statEnabledLabel", key: "statEnabledLabel" },
165
173
  { id: "statServiceLabel", key: "statServiceLabel" },
166
- { id: "openImConfigSummary", key: "configJson" },
167
- { id: "claudeSettingsSummary", key: "claudeSettingsLabel" },
174
+ { id: "configFilesTitle", key: "configFilesTitle" },
175
+ { id: "configFilesHint", key: "configFilesHint" },
176
+ { id: "openImConfigCardTitle", key: "configJson" },
177
+ { id: "openImConfigCardHint", key: "openImConfigCardHint" },
178
+ { id: "claudeSettingsCardTitle", key: "claudeSettingsLabel" },
179
+ { id: "claudeSettingsCardHint", key: "claudeSettingsCardHint" },
180
+ { id: "claudeJsonShortcutHint", key: "claudeJsonShortcutHint" },
168
181
  { id: "formatJsonButtonText", key: "formatJson" },
169
182
  { id: "resetJsonButtonText", key: "resetJson" },
170
183
  { id: "saveClaudeSettingsBtnText", key: "saveBtn" },
171
184
  { id: "saveOpenImConfigBtnText", key: "saveBtn" },
172
- { id: "wizardTitle", key: "wizardTitle" },
173
- { id: "wizardStep1Title", key: "wizardStep1Title" },
174
- { id: "wizardStep1Desc", key: "wizardStep1Desc" },
175
- { id: "wizardStep2Title", key: "wizardStep2Title" },
176
- { id: "wizardStep2Desc", key: "wizardStep2Desc" },
177
- { id: "wizardStep3Title", key: "wizardStep3Title" },
178
- { id: "wizardStep3Desc", key: "wizardStep3Desc" },
179
- { id: "wizardStep4Title", key: "wizardStep4Title" },
180
- { id: "wizardStep4Desc", key: "wizardStep4Desc" },
181
- { id: "wizardJumpPlatforms", key: "wizardJumpPlatforms" },
182
- { id: "wizardJumpAi", key: "wizardJumpAi" },
183
- { id: "wizardJumpService", key: "wizardJumpService" },
184
- { id: "wizardJumpService2", key: "wizardJumpService" },
185
- { id: "onboardingTitle", key: "onboardingTitle" },
186
- { id: "onboardingDismiss", key: "onboardingDismiss" },
187
- { id: "onboardingReadme", key: "onboardingReadme" },
188
185
  ],
189
186
  platformLabels: {
190
187
  enabled: { suffix: "-label", key: "enabled" },
@@ -232,14 +229,11 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
232
229
  { id: "aiCommonTitle", key: "aiCommonTitle" },
233
230
  { id: "ai-aiCommand-label", key: "aiTool" },
234
231
  { id: "ai-claudeWorkDir-label", key: "workDir" },
235
- { id: "ai-claudeTimeoutMs-label", key: "claudeTimeout" },
236
232
  { id: "ai-claudeConfigPath-label", key: "claudeConfigPath" },
237
233
  { id: "ai-claudeProxy-label", key: "claudeProxy" },
238
234
  { id: "ai-codexCliPath-label", key: "codexCli" },
239
- { id: "ai-codexTimeoutMs-label", key: "codexTimeout" },
240
235
  { id: "ai-codexProxy-label", key: "codexProxy" },
241
236
  { id: "ai-codebuddyCliPath-label", key: "codebuddyCli" },
242
- { id: "ai-codebuddyTimeoutMs-label", key: "codebuddyTimeout" },
243
237
  { id: "ai-hookPort-label", key: "hookPort" },
244
238
  { id: "ai-logLevel-label", key: "logLevel" },
245
239
  ],
@@ -248,6 +242,10 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
248
242
  { id: "saveButton", key: "save" },
249
243
  { id: "startButton", key: "start" },
250
244
  { id: "stopButton", key: "stop" },
245
+ { id: "headerValidateButton", key: "validate" },
246
+ { id: "headerSaveButton", key: "save" },
247
+ { id: "headerStartButton", key: "start" },
248
+ { id: "headerStopButton", key: "stop" },
251
249
  ],
252
250
  testButtons: [
253
251
  { prefix: "test-", key: "test" },
@@ -308,9 +306,6 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
308
306
  if (tipEl) tipEl.innerHTML = t(tipKey);
309
307
  });
310
308
 
311
- const onboardBody = el("onboardingBody");
312
- if (onboardBody) onboardBody.innerHTML = t("onboardingBody");
313
-
314
309
  // AI labels
315
310
  LANGUAGE_UPDATES.aiLabels.forEach(({ id, key }) => {
316
311
  const label = el(id + "-label");
@@ -338,13 +333,8 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
338
333
  const darkModeToggle = el("darkModeToggle");
339
334
  if (darkModeToggle) darkModeToggle.setAttribute("aria-label", t("darkModeToggle"));
340
335
 
341
- const readmeLink = el("onboardingReadmeLink");
342
- if (readmeLink && readmeLink instanceof HTMLAnchorElement) {
343
- readmeLink.href = isZh
344
- ? "https://github.com/wu529778790/open-im/blob/main/README.zh-CN.md"
345
- : "https://github.com/wu529778790/open-im/blob/main/README.md";
346
- }
347
- updateSetupWizard();
336
+ const headerToolbar = el("headerToolbar");
337
+ if (headerToolbar) headerToolbar.setAttribute("aria-label", t("headerToolbarAria"));
348
338
  }
349
339
 
350
340
  // AI tool switcher
@@ -376,34 +366,6 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
376
366
  const liveSummary = el("liveSummary");
377
367
  if (liveSummary) liveSummary.textContent = summary;
378
368
  updateAiToolVisibility();
379
- updateSetupWizard();
380
- }
381
-
382
- function hasReadyPlatform() {
383
- return platformDefinitions.some((platform) => {
384
- if (!getChecked(platform.key + "-enabled")) return false;
385
- return platform.requiredFields.every((field) => getValue(platform.key + "-" + field).trim().length > 0);
386
- });
387
- }
388
-
389
- function aiStepComplete() {
390
- const cmd = getValue("ai-aiCommand");
391
- if (cmd === "codex") return getValue("ai-codexCliPath").trim().length > 0;
392
- if (cmd === "codebuddy") return getValue("ai-codebuddyCliPath").trim().length > 0;
393
- return true;
394
- }
395
-
396
- function updateSetupWizard() {
397
- const s1 = hasReadyPlatform();
398
- const s2 = aiStepComplete();
399
- const s3 = sessionStorage.getItem(STORAGE_KEY_SAVED_SESSION) === "1";
400
- const s4 = Boolean(lastHealthPayload && lastHealthPayload.serviceStatus && lastHealthPayload.serviceStatus.running);
401
- const done = [s1, s2, s3, s4];
402
- for (let i = 0; i < 4; i += 1) {
403
- const step = el("wizard-step-" + (i + 1));
404
- if (step) step.classList.toggle("wizard-step--done", done[i]);
405
- setText("wizardStep" + (i + 1) + "Status", done[i] ? t("wizardStatusDone") : t("wizardStatusTodo"));
406
- }
407
369
  }
408
370
 
409
371
  function collectClientValidationErrors() {
@@ -492,7 +454,6 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
492
454
  el("statServiceValue").textContent = serviceStatus.running ? t("serviceRunningShort") : t("serviceIdleShort");
493
455
 
494
456
  lastHealthPayload = data;
495
- updateSetupWizard();
496
457
 
497
458
  return data;
498
459
  } catch (error) {
@@ -502,7 +463,7 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
502
463
 
503
464
  // Navigation
504
465
  function setActiveNav(targetId) {
505
- ["navOverviewBtn","navPlatformsBtn","navAiBtn","navServiceBtn"].forEach((id) => {
466
+ ["navOverviewBtn","navPlatformsBtn","navConfigFilesBtn","navAiBtn","navServiceBtn"].forEach((id) => {
506
467
  const btn = el(id);
507
468
  if (btn) btn.classList.toggle("active", id === targetId);
508
469
  });
@@ -635,14 +596,11 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
635
596
  const AI_FIELD_MAPPINGS = [
636
597
  { id: "ai-aiCommand", key: "aiCommand" },
637
598
  { id: "ai-claudeWorkDir", key: "claudeWorkDir" },
638
- { id: "ai-claudeTimeoutMs", key: "claudeTimeoutMs" },
639
599
  { id: "ai-claudeConfigPath", key: "claudeConfigPath" },
640
600
  { id: "ai-claudeProxy", key: "claudeProxy" },
641
601
  { id: "ai-codexCliPath", key: "codexCliPath" },
642
- { id: "ai-codexTimeoutMs", key: "codexTimeoutMs" },
643
602
  { id: "ai-codexProxy", key: "codexProxy" },
644
603
  { id: "ai-codebuddyCliPath", key: "codebuddyCliPath" },
645
- { id: "ai-codebuddyTimeoutMs", key: "codebuddyTimeoutMs" },
646
604
  { id: "ai-hookPort", key: "hookPort" },
647
605
  { id: "ai-logLevel", key: "logLevel" },
648
606
  ];
@@ -726,21 +684,6 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
726
684
  }
727
685
  });
728
686
 
729
- // Open-im config.json: load when expanded
730
- const openImConfigContainer = document.getElementById("openImConfigContainer");
731
- if (openImConfigContainer && openImConfigContainer instanceof HTMLDetailsElement) {
732
- openImConfigContainer.addEventListener("toggle", () => {
733
- if (openImConfigContainer.open) void loadOpenImConfig();
734
- });
735
- }
736
- // Claude settings.json: load when expanded
737
- const claudeSettingsContainer = document.getElementById("claudeSettingsContainer");
738
- if (claudeSettingsContainer && claudeSettingsContainer instanceof HTMLDetailsElement) {
739
- claudeSettingsContainer.addEventListener("toggle", () => {
740
- if (claudeSettingsContainer.open) void loadClaudeSettings();
741
- });
742
- }
743
-
744
687
  el("saveClaudeSettingsBtn").onclick = async () => {
745
688
  try {
746
689
  await saveClaudeSettings();
@@ -789,29 +732,10 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
789
732
  // Navigation
790
733
  el("navOverviewBtn").onclick = () => scrollToSection("dashboardSection", "navOverviewBtn");
791
734
  el("navPlatformsBtn").onclick = () => scrollToSection("configSection", "navPlatformsBtn");
735
+ el("navConfigFilesBtn").onclick = () => scrollToSection("configFilesSection", "navConfigFilesBtn");
792
736
  el("navAiBtn").onclick = () => scrollToSection("aiSection", "navAiBtn");
793
737
  el("navServiceBtn").onclick = () => scrollToSection("serviceSection", "navServiceBtn");
794
738
 
795
- document.querySelectorAll(".wizard-jump").forEach((jumpBtn) => {
796
- jumpBtn.addEventListener("click", () => {
797
- const sec = jumpBtn.getAttribute("data-section");
798
- const nav = jumpBtn.getAttribute("data-nav");
799
- if (sec && nav) scrollToSection(sec, nav);
800
- });
801
- });
802
-
803
- const onboardBackdrop = el("onboardingBackdrop");
804
- const onboardDismiss = el("onboardingDismiss");
805
- if (onboardBackdrop && onboardDismiss) {
806
- if (!localStorage.getItem(STORAGE_KEY_ONBOARDING)) {
807
- onboardBackdrop.classList.remove("hidden");
808
- }
809
- onboardDismiss.onclick = () => {
810
- localStorage.setItem(STORAGE_KEY_ONBOARDING, "1");
811
- onboardBackdrop.classList.add("hidden");
812
- };
813
- }
814
-
815
739
  // Language toggle
816
740
  el("langButton").onclick = () => {
817
741
  currentLang = currentLang === "zh" ? "en" : "zh";
@@ -825,19 +749,25 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
825
749
  el("darkModeToggle").onclick = toggleDarkMode;
826
750
  updateDarkMode(); // Initialize dark mode on load
827
751
 
828
- // Service buttons
829
- el("validateButton").onclick = validate;
830
- el("saveButton").onclick = async () => {
752
+ // Service buttons (page footer + sticky header toolbar)
753
+ const onSaveClick = async () => {
831
754
  if (!validateClientSideOrAbort()) return;
832
755
  await saveClaudeSettings();
833
756
  await save();
834
757
  };
835
- el("startButton").onclick = async () => {
758
+ const onStartClick = async () => {
836
759
  if (!validateClientSideOrAbort()) return;
837
760
  await saveClaudeSettings();
838
761
  await startService();
839
762
  };
763
+ el("validateButton").onclick = validate;
764
+ el("headerValidateButton").onclick = validate;
765
+ el("saveButton").onclick = onSaveClick;
766
+ el("headerSaveButton").onclick = onSaveClick;
767
+ el("startButton").onclick = onStartClick;
768
+ el("headerStartButton").onclick = onStartClick;
840
769
  el("stopButton").onclick = stopService;
770
+ el("headerStopButton").onclick = stopService;
841
771
 
842
772
  // Platform test buttons
843
773
  platformKeys.forEach((platform) => {
@@ -871,9 +801,7 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
871
801
  await saveOpenImConfig();
872
802
  // Then save form data
873
803
  await request("/api/config/save?final=1", { method: "POST", body: JSON.stringify(payload()) });
874
- sessionStorage.setItem(STORAGE_KEY_SAVED_SESSION, "1");
875
804
  setMessage(t("saveOk"), "success");
876
- updateSetupWizard();
877
805
  } catch (error) {
878
806
  setMessage(error.message || String(error), "error");
879
807
  } finally {
@@ -989,11 +917,8 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
989
917
  ai: {
990
918
  aiCommand: getValue("ai-aiCommand"),
991
919
  claudeWorkDir: getValue("ai-claudeWorkDir"),
992
- claudeTimeoutMs: getNumber("ai-claudeTimeoutMs"),
993
920
  claudeConfigPath: getValue("ai-claudeConfigPath"),
994
921
  claudeProxy: getValue("ai-claudeProxy"),
995
- codexTimeoutMs: getNumber("ai-codexTimeoutMs"),
996
- codebuddyTimeoutMs: getNumber("ai-codebuddyTimeoutMs"),
997
922
  codexCliPath: getValue("ai-codexCliPath"),
998
923
  codexProxy: getValue("ai-codexProxy"),
999
924
  codebuddyCliPath: getValue("ai-codebuddyCliPath"),