codexmate 0.0.12 → 0.0.13

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.
package/web-ui/index.html CHANGED
@@ -64,6 +64,15 @@
64
64
  aria-controls="panel-sessions"
65
65
  :class="{ active: mainTab === 'sessions' }"
66
66
  @click="switchMainTab('sessions')">会话浏览</button>
67
+ <button class="top-tab"
68
+ id="tab-settings"
69
+ role="tab"
70
+ :tabindex="mainTab === 'settings' ? 0 : -1"
71
+ :aria-selected="mainTab === 'settings'"
72
+ :aria-pressed="mainTab === 'settings'"
73
+ aria-controls="panel-settings"
74
+ :class="{ active: mainTab === 'settings' }"
75
+ @click="switchMainTab('settings')">设置</button>
67
76
  </div>
68
77
 
69
78
  <div :class="['app-shell', { standalone: sessionStandalone }]">
@@ -147,17 +156,35 @@
147
156
  </div>
148
157
  </button>
149
158
  </div>
159
+
160
+ <div class="side-section" role="tablist" aria-label="设置">
161
+ <div class="side-section-title">设置</div>
162
+ <button
163
+ role="tab"
164
+ id="side-tab-settings"
165
+ aria-controls="panel-settings"
166
+ :tabindex="mainTab === 'settings' ? 0 : -1"
167
+ :aria-selected="mainTab === 'settings'"
168
+ :aria-pressed="mainTab === 'settings'"
169
+ :class="['side-item', { active: mainTab === 'settings' }]"
170
+ @click="switchMainTab('settings')">
171
+ <div class="side-item-title">设置</div>
172
+ <div class="side-item-meta">
173
+ <span>数据管理 / 下载</span>
174
+ </div>
175
+ </button>
176
+ </div>
150
177
  </aside>
151
178
  <main class="main-panel">
152
179
  <div class="panel-header" v-if="!sessionStandalone">
153
180
  <h1 class="main-title">
154
- {{ mainTab === 'config' ? '配置中心' : '会话浏览' }}
181
+ {{ mainTab === 'config' ? '配置中心' : (mainTab === 'sessions' ? '会话浏览' : '设置') }}
155
182
  </h1>
156
183
  <p class="subtitle" v-if="mainTab === 'config'">
157
184
  配置中枢:管理 Codex / Claude / OpenClaw
158
185
  <span class="sr-only">本地配置中枢,统一管理 Codex / Claude Code / OpenClaw。</span>
159
186
  </p>
160
- <p class="subtitle" v-else>
187
+ <p class="subtitle" v-else-if="mainTab === 'sessions'">
161
188
  浏览、导出或独立查看 Codex / Claude 会话记录。
162
189
  </p>
163
190
  </div>
@@ -318,20 +345,117 @@
318
345
 
319
346
  <div class="selector-section">
320
347
  <div class="selector-header">
321
- <span class="selector-title">配置健康检查</span>
348
+ <span class="selector-title">配置维护</span>
322
349
  </div>
323
- <button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
324
- {{ healthCheckLoading ? '检查中...' : '运行检查' }}
350
+ <div class="config-template-hint">备份当前 config.toml 后写入默认配置。</div>
351
+ <button class="btn-tool" @click="resetConfig" :disabled="resetConfigLoading || loading || !!initError">
352
+ {{ resetConfigLoading ? '重装中...' : '一键重装配置' }}
325
353
  </button>
326
354
  </div>
327
355
 
328
356
  <div class="selector-section">
329
357
  <div class="selector-header">
330
- <span class="selector-title">配置维护</span>
358
+ <span class="selector-title">Codex 认证文件</span>
331
359
  </div>
332
- <div class="config-template-hint">备份当前 config.toml 后写入默认配置。</div>
333
- <button class="btn-tool" @click="resetConfig" :disabled="resetConfigLoading || loading || !!initError">
334
- {{ resetConfigLoading ? '重装中...' : '一键重装配置' }}
360
+ <div class="config-template-hint">
361
+ 上传 JSON 后可切换账号(会写入 <code>~/.codex/auth.json</code>)。
362
+ </div>
363
+ <button class="btn-tool" @click="triggerCodexAuthUpload" :disabled="codexAuthImportLoading || loading || !!initError">
364
+ {{ codexAuthImportLoading ? '上传中...' : '上传认证文件' }}
365
+ </button>
366
+ <input
367
+ ref="codexAuthImportInput"
368
+ type="file"
369
+ accept=".json,application/json"
370
+ style="display:none"
371
+ @change="handleCodexAuthImportChange">
372
+ <div v-if="codexAuthProfiles.length === 0" class="form-hint">暂无认证文件。</div>
373
+ <div v-else class="auth-profile-list">
374
+ <div class="auth-profile-item" v-for="profile in codexAuthProfiles" :key="'auth-' + profile.name">
375
+ <div class="auth-profile-header">
376
+ <div class="auth-profile-main">
377
+ <div class="auth-profile-title">{{ profile.name }}</div>
378
+ <div class="auth-profile-meta">
379
+ <span class="provider-source">{{ profile.type || 'unknown' }}</span>
380
+ <span :class="['pill', profile.current ? 'configured' : 'empty']">
381
+ {{ profile.current ? '当前' : '备用' }}
382
+ </span>
383
+ </div>
384
+ </div>
385
+ <div class="auth-profile-actions">
386
+ <button
387
+ class="btn-mini"
388
+ :disabled="profile.current || codexAuthSwitching[profile.name]"
389
+ @click="switchCodexAuthProfile(profile.name)">
390
+ {{ codexAuthSwitching[profile.name] ? '切换中...' : (profile.current ? '当前使用' : '切换账号') }}
391
+ </button>
392
+ <button
393
+ class="btn-mini delete"
394
+ :disabled="codexAuthDeleting[profile.name]"
395
+ @click="deleteCodexAuthProfile(profile.name)">
396
+ {{ codexAuthDeleting[profile.name] ? '删除中...' : '删除' }}
397
+ </button>
398
+ </div>
399
+ </div>
400
+ <div class="auth-profile-grid">
401
+ <div v-if="profile.email" class="auth-profile-row">
402
+ <span class="auth-profile-key">email</span>
403
+ <span class="auth-profile-value">{{ profile.email }}</span>
404
+ </div>
405
+ <div v-if="profile.accountId" class="auth-profile-row">
406
+ <span class="auth-profile-key">account_id</span>
407
+ <span class="auth-profile-value">{{ profile.accountId }}</span>
408
+ </div>
409
+ <div v-if="profile.expired" class="auth-profile-row">
410
+ <span class="auth-profile-key">expired</span>
411
+ <span class="auth-profile-value">{{ profile.expired }}</span>
412
+ </div>
413
+ </div>
414
+ </div>
415
+ </div>
416
+ </div>
417
+
418
+ <div class="selector-section">
419
+ <div class="selector-header">
420
+ <span class="selector-title">内建代理</span>
421
+ </div>
422
+ <button class="btn-mini" @click="showProxyAdvanced = !showProxyAdvanced">
423
+ {{ showProxyAdvanced ? '收起高级设置' : '展开高级设置(端口/鉴权)' }}
424
+ </button>
425
+ <div v-if="showProxyAdvanced">
426
+ <div class="list-row">
427
+ <label class="form-label">上游 Provider</label>
428
+ <select class="form-input" v-model="proxySettings.provider" @change="saveProxySettings({ silent: true })">
429
+ <option value="">自动(当前 provider)</option>
430
+ <option v-for="name in proxyProviderOptions" :key="'proxy-provider-' + name" :value="name">{{ name }}</option>
431
+ </select>
432
+ <label class="form-label">端口</label>
433
+ <input v-model.number="proxySettings.port" class="form-input" type="number" min="1" max="65535" placeholder="8318" @change="saveProxySettings({ silent: true })">
434
+ </div>
435
+ <div class="list-row">
436
+ <label class="form-label">鉴权来源</label>
437
+ <select class="form-input" v-model="proxySettings.authSource" @change="saveProxySettings({ silent: true })">
438
+ <option value="provider">provider 优先</option>
439
+ <option value="profile">仅当前认证文件</option>
440
+ <option value="none">不注入鉴权头</option>
441
+ </select>
442
+ <label class="form-label">超时(ms)</label>
443
+ <input v-model.number="proxySettings.timeoutMs" class="form-input" type="number" min="1000" step="500" placeholder="30000" @change="saveProxySettings({ silent: true })">
444
+ </div>
445
+ <div class="form-hint">高级参数修改后自动保存。</div>
446
+ </div>
447
+ <div class="config-template-hint" v-if="proxyRuntime">
448
+ 运行中:入口 <code>{{ proxyRuntimeDisplayProvider }}</code>
449
+ <span v-if="proxyRuntime.upstreamProvider">(上游 <code>{{ proxyRuntime.upstreamProvider }}</code>)</span>
450
+ </div>
451
+ </div>
452
+
453
+ <div class="selector-section">
454
+ <div class="selector-header">
455
+ <span class="selector-title">配置健康检查</span>
456
+ </div>
457
+ <button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
458
+ {{ healthCheckLoading ? '检查中...' : '运行检查' }}
335
459
  </button>
336
460
  </div>
337
461
 
@@ -342,13 +466,18 @@
342
466
  <div class="card-leading">
343
467
  <div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}</div>
344
468
  <div class="card-content">
345
- <div class="card-title">{{ provider.name }}</div>
346
- <div class="card-subtitle">{{ provider.url || '未设置 URL' }}</div>
469
+ <div class="card-title">
470
+ <span>{{ provider.name }}</span>
471
+ <span v-if="provider.readOnly" class="provider-readonly-badge">内建</span>
472
+ </div>
473
+ <div class="card-subtitle">
474
+ {{ provider.readOnly ? '系统内建本地代理(自动维护)' : (provider.url || '未设置 URL') }}
475
+ </div>
347
476
  </div>
348
477
  </div>
349
478
  <div class="card-trailing">
350
- <span :class="['pill', provider.hasKey ? 'configured' : 'empty']">
351
- {{ provider.hasKey ? '已配置' : '未配置' }}
479
+ <span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">
480
+ {{ providerPillText(provider) }}
352
481
  </span>
353
482
  <span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">
354
483
  {{ formatLatency(speedResults[provider.name]) }}
@@ -359,20 +488,38 @@
359
488
  <path d="M13 2L3 14h7l-1 8 12-14h-7l-1-6z"/>
360
489
  </svg>
361
490
  </button>
362
- <button class="card-action-btn" :class="{ loading: providerShareLoading[provider.name] }" @click="copyProviderShareCommand(provider)" title="分享导入命令">
491
+ <button
492
+ v-if="!provider.readOnly"
493
+ class="card-action-btn"
494
+ :class="{ loading: providerShareLoading[provider.name], disabled: !shouldAllowProviderShare(provider) }"
495
+ :disabled="!shouldAllowProviderShare(provider)"
496
+ @click="copyProviderShareCommand(provider)"
497
+ :title="shouldAllowProviderShare(provider) ? '分享导入命令' : '本地入口不可分享'">
363
498
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
364
499
  <path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
365
500
  <path d="M16 6l-4-4-4 4"/>
366
501
  <path d="M12 2v14"/>
367
502
  </svg>
368
503
  </button>
369
- <button class="card-action-btn" @click="openEditModal(provider)" title="编辑">
504
+ <button
505
+ v-if="!provider.readOnly"
506
+ class="card-action-btn"
507
+ :class="{ disabled: !shouldShowProviderEdit(provider) }"
508
+ :disabled="!shouldShowProviderEdit(provider)"
509
+ @click="openEditModal(provider)"
510
+ :title="shouldShowProviderEdit(provider) ? '编辑' : '不可编辑'">
370
511
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
371
512
  <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
372
513
  <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
373
514
  </svg>
374
515
  </button>
375
- <button class="card-action-btn delete" @click="deleteProvider(provider.name)" title="删除">
516
+ <button
517
+ v-if="!provider.readOnly"
518
+ class="card-action-btn delete"
519
+ :class="{ disabled: !shouldShowProviderDelete(provider) }"
520
+ :disabled="!shouldShowProviderDelete(provider)"
521
+ @click="deleteProvider(provider.name)"
522
+ :title="shouldShowProviderDelete(provider) ? '删除' : '不可删除'">
376
523
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
377
524
  <path d="M3 6h18"/>
378
525
  <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
@@ -716,13 +863,6 @@
716
863
  <button class="btn-session-refresh" @click="loadActiveSessionDetail" :disabled="sessionDetailLoading || !activeSession">
717
864
  {{ sessionDetailLoading ? '加载中...' : '刷新内容' }}
718
865
  </button>
719
- <button
720
- v-if="isCloneAvailable(activeSession)"
721
- class="btn-session-clone"
722
- @click="cloneSession(activeSession)"
723
- :disabled="!activeSession || sessionsLoading || sessionCloning[getSessionExportKey(activeSession)]">
724
- {{ (activeSession && sessionCloning[getSessionExportKey(activeSession)]) ? '克隆中...' : '克隆会话' }}
725
- </button>
726
866
  <button
727
867
  v-if="isDeleteAvailable(activeSession)"
728
868
  class="btn-session-delete"
@@ -787,6 +927,49 @@
787
927
  </div>
788
928
  </div>
789
929
 
930
+ <!-- 设置面板 -->
931
+ <div
932
+ v-show="mainTab === 'settings'"
933
+ class="mode-content"
934
+ id="panel-settings"
935
+ role="tabpanel"
936
+ :aria-labelledby="'tab-settings'">
937
+ <div class="selector-section">
938
+ <div class="selector-header">
939
+ <span class="selector-title">Claude 配置</span>
940
+ </div>
941
+ <button class="btn-tool" @click="downloadClaudeDirectory" :disabled="claudeDownloadLoading">
942
+ {{ claudeDownloadLoading ? ('备份中 ' + claudeDownloadProgress + '%') : '一键备份 ~/.claude' }}
943
+ </button>
944
+ <button class="btn-tool" @click="triggerClaudeImport" :disabled="claudeImportLoading">
945
+ {{ claudeImportLoading ? '导入中...' : '导入 ~/.claude 备份' }}
946
+ </button>
947
+ <input
948
+ ref="claudeImportInput"
949
+ class="sr-only"
950
+ type="file"
951
+ accept=".zip"
952
+ @change="handleClaudeImportChange">
953
+ </div>
954
+ <div class="selector-section">
955
+ <div class="selector-header">
956
+ <span class="selector-title">Codex 配置</span>
957
+ </div>
958
+ <button class="btn-tool" @click="downloadCodexDirectory" :disabled="codexDownloadLoading">
959
+ {{ codexDownloadLoading ? ('备份中 ' + codexDownloadProgress + '%') : '一键备份 ~/.codex' }}
960
+ </button>
961
+ <button class="btn-tool" @click="triggerCodexImport" :disabled="codexImportLoading">
962
+ {{ codexImportLoading ? '导入中...' : '导入 ~/.codex 备份' }}
963
+ </button>
964
+ <input
965
+ ref="codexImportInput"
966
+ class="sr-only"
967
+ type="file"
968
+ accept=".zip"
969
+ @change="handleCodexImportChange">
970
+ </div>
971
+ </div>
972
+
790
973
  <!-- 加载状态 -->
791
974
  <div v-if="loading" class="state-message">
792
975
  加载配置中...
@@ -799,6 +982,64 @@
799
982
  </div>
800
983
 
801
984
  </main>
985
+ <aside class="status-inspector" v-if="!sessionStandalone" aria-label="当前状态检查器">
986
+ <div class="inspector-head">
987
+ <div class="inspector-title">当前状态</div>
988
+ <div class="inspector-subtitle">固定可见 · 实时同步</div>
989
+ </div>
990
+
991
+ <section class="inspector-group" aria-label="当前上下文">
992
+ <div class="inspector-group-title">当前上下文</div>
993
+ <div class="inspector-kv">
994
+ <span class="key">主标签</span>
995
+ <span class="value">{{ inspectorMainTabLabel }}</span>
996
+ <span class="key">配置模式</span>
997
+ <span class="value">{{ inspectorConfigModeLabel }}</span>
998
+ <span class="key">当前配置</span>
999
+ <span class="value">{{ inspectorCurrentConfigLabel }}</span>
1000
+ <span class="key">当前模型</span>
1001
+ <span class="value">{{ inspectorCurrentModelLabel }}</span>
1002
+ </div>
1003
+ </section>
1004
+
1005
+ <section class="inspector-group" aria-label="生效与一致性">
1006
+ <div class="inspector-group-title">生效与一致性</div>
1007
+ <div class="inspector-kv">
1008
+ <span class="key">模板状态</span>
1009
+ <span class="value">{{ inspectorTemplateStatus }}</span>
1010
+ <span class="key">运行状态</span>
1011
+ <span class="value">{{ inspectorBusyStatus }}</span>
1012
+ <span class="key">最近提示</span>
1013
+ <span class="value">{{ inspectorMessageSummary }}</span>
1014
+ </div>
1015
+ </section>
1016
+
1017
+ <section class="inspector-group" aria-label="会话摘要">
1018
+ <div class="inspector-group-title">会话摘要</div>
1019
+ <div class="inspector-kv">
1020
+ <span class="key">当前来源</span>
1021
+ <span class="value">{{ inspectorSessionSourceLabel }}</span>
1022
+ <span class="key">路径过滤</span>
1023
+ <span class="value">{{ inspectorSessionPathLabel }}</span>
1024
+ <span class="key">检索条件</span>
1025
+ <span class="value">{{ inspectorSessionQueryLabel }}</span>
1026
+ <span class="key">结果数量</span>
1027
+ <span class="value">{{ sessionsList.length }}</span>
1028
+ </div>
1029
+ </section>
1030
+
1031
+ <section class="inspector-group" aria-label="健康提示">
1032
+ <div class="inspector-group-title">健康提示</div>
1033
+ <div class="inspector-kv">
1034
+ <span class="key">配置读取</span>
1035
+ <span :class="['value', 'tone-' + inspectorHealthTone]">{{ inspectorHealthStatus }}</span>
1036
+ <span class="key">模型加载</span>
1037
+ <span class="value">{{ inspectorModelLoadStatus }}</span>
1038
+ <span class="key">代理状态</span>
1039
+ <span class="value">{{ inspectorProxyStatus }}</span>
1040
+ </div>
1041
+ </section>
1042
+ </aside>
802
1043
  </div>
803
1044
 
804
1045
  <!-- 添加提供商模态框 -->
@@ -830,17 +1071,60 @@
830
1071
  <div class="modal">
831
1072
  <div class="modal-header">
832
1073
  <div class="modal-title">安装 CLI</div>
833
- <button class="btn-mini" @click="closeInstallModal">关闭</button>
834
1074
  </div>
1075
+
1076
+ <div class="list-row">
1077
+ <label class="form-label">包管理器</label>
1078
+ <select class="form-input" v-model="installPackageManager">
1079
+ <option value="npm">npm</option>
1080
+ <option value="pnpm">pnpm</option>
1081
+ <option value="bun">bun</option>
1082
+ </select>
1083
+ <label class="form-label">镜像源</label>
1084
+ <div class="install-action-tabs">
1085
+ <button class="btn-mini" :class="{ active: installRegistryPreset === 'default' }" @click="setInstallRegistryPreset('default')">官方</button>
1086
+ <button class="btn-mini" :class="{ active: installRegistryPreset === 'npmmirror' }" @click="setInstallRegistryPreset('npmmirror')">npmmirror</button>
1087
+ <button class="btn-mini" :class="{ active: installRegistryPreset === 'tencent' }" @click="setInstallRegistryPreset('tencent')">腾讯云</button>
1088
+ <button class="btn-mini" :class="{ active: installRegistryPreset === 'custom' }" @click="setInstallRegistryPreset('custom')">自定义</button>
1089
+ </div>
1090
+ <input
1091
+ v-if="installRegistryPreset === 'custom'"
1092
+ v-model="installRegistryCustom"
1093
+ class="form-input install-registry-input"
1094
+ placeholder="https://registry.example.com">
1095
+ <div class="form-hint install-registry-hint" v-if="installRegistryPreview">
1096
+ 当前命令会附加:--registry={{ installRegistryPreview }}
1097
+ </div>
1098
+ <div class="form-hint install-registry-hint" v-else-if="installRegistryPreset === 'custom'">
1099
+ 请输入完整 URL(含 http/https)后将自动附加到安装/升级命令。
1100
+ </div>
1101
+ <label class="form-label">操作</label>
1102
+ <div class="install-action-tabs">
1103
+ <button class="btn-mini" :class="{ active: installCommandAction === 'install' }" @click="setInstallCommandAction('install')">安装</button>
1104
+ <button class="btn-mini" :class="{ active: installCommandAction === 'update' }" @click="setInstallCommandAction('update')">升级</button>
1105
+ <button class="btn-mini" :class="{ active: installCommandAction === 'uninstall' }" @click="setInstallCommandAction('uninstall')">卸载</button>
1106
+ </div>
1107
+ </div>
1108
+
835
1109
  <div class="install-list">
836
1110
  <div
837
1111
  class="install-row"
838
- v-for="cmd in installCommands"
839
- :key="cmd">
840
- <code class="install-command">{{ cmd }}</code>
841
- <button class="btn-mini" @click="copyInstallCommand(cmd)">复制</button>
1112
+ v-for="target in installTargetCards"
1113
+ :key="'install-command-' + target.id + '-' + installCommandAction">
1114
+ <div class="install-row-main">
1115
+ <div class="install-row-title">{{ target.name }}</div>
1116
+ <code class="install-command">{{ target.command }}</code>
1117
+ </div>
1118
+ <button class="btn-mini" :disabled="!target.command" @click="copyInstallCommand(target.command)">复制</button>
842
1119
  </div>
843
1120
  </div>
1121
+
1122
+ <div class="install-help">
1123
+ <div class="form-label">常见失败自救</div>
1124
+ <ul class="install-help-list">
1125
+ <li v-for="tip in installTroubleshootingTips" :key="tip">{{ tip }}</li>
1126
+ </ul>
1127
+ </div>
844
1128
  </div>
845
1129
  </div>
846
1130
 
@@ -1256,41 +1540,51 @@
1256
1540
  </div>
1257
1541
 
1258
1542
  <div v-if="showAgentsModal" class="modal-overlay" @click.self="closeAgentsModal">
1259
- <div class="modal modal-wide">
1260
- <div class="modal-header">
1543
+ <div class="modal modal-wide modal-editor">
1544
+ <div class="modal-header modal-editor-header">
1261
1545
  <div class="modal-title">{{ agentsModalTitle }}</div>
1262
- <button
1263
- class="btn-mini btn-modal-copy"
1264
- @click="copyAgentsContent"
1265
- :disabled="agentsLoading">
1266
- 复制
1267
- </button>
1546
+ <div class="modal-header-actions">
1547
+ <button
1548
+ class="btn-mini btn-modal-copy"
1549
+ @click="exportAgentsContent"
1550
+ :disabled="agentsLoading">
1551
+ 导出
1552
+ </button>
1553
+ <button
1554
+ class="btn-mini btn-modal-copy"
1555
+ @click="copyAgentsContent"
1556
+ :disabled="agentsLoading">
1557
+ 复制
1558
+ </button>
1559
+ </div>
1268
1560
  </div>
1269
1561
 
1270
- <div class="form-group">
1271
- <label class="form-label">目标文件</label>
1272
- <div class="form-hint">
1273
- {{ agentsPath || '未加载' }}
1274
- <span v-if="agentsPath">
1275
- ({{ agentsExists ? '已存在' : '不存在,将在保存时创建' }})
1276
- </span>
1562
+ <div class="modal-editor-body">
1563
+ <div class="form-group">
1564
+ <label class="form-label">目标文件</label>
1565
+ <div class="form-hint">
1566
+ {{ agentsPath || '未加载' }}
1567
+ <span v-if="agentsPath">
1568
+ ({{ agentsExists ? '已存在' : '不存在,将在保存时创建' }})
1569
+ </span>
1570
+ </div>
1277
1571
  </div>
1278
- </div>
1279
1572
 
1280
- <div class="form-group">
1281
- <label class="form-label">AGENTS.md 内容</label>
1282
- <textarea
1283
- v-model="agentsContent"
1284
- class="form-input template-editor"
1285
- spellcheck="false"
1286
- :readonly="agentsLoading"
1287
- placeholder="在这里编辑 AGENTS.md 内容"></textarea>
1288
- <div class="template-editor-warning">
1289
- {{ agentsModalHint }}
1573
+ <div class="form-group">
1574
+ <label class="form-label">AGENTS.md 内容</label>
1575
+ <textarea
1576
+ v-model="agentsContent"
1577
+ class="form-input template-editor"
1578
+ spellcheck="false"
1579
+ :readonly="agentsLoading"
1580
+ placeholder="在这里编辑 AGENTS.md 内容"></textarea>
1581
+ <div class="template-editor-warning">
1582
+ {{ agentsModalHint }}
1583
+ </div>
1290
1584
  </div>
1291
1585
  </div>
1292
1586
 
1293
- <div class="btn-group">
1587
+ <div class="btn-group modal-editor-footer">
1294
1588
  <button class="btn btn-cancel" @click="closeAgentsModal">取消</button>
1295
1589
  <button class="btn btn-confirm" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading">
1296
1590
  {{ agentsSaving ? '保存中...' : '保存' }}
@@ -1308,3 +1602,4 @@
1308
1602
  <script type="module" src="web-ui/app.js"></script>
1309
1603
  </body>
1310
1604
  </html>
1605
+