codexmate 0.0.50 → 0.0.52

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.
@@ -244,9 +244,6 @@ function createAgentsFileController(deps = {}) {
244
244
  if (context === 'claude-md') {
245
245
  readResult = readClaudeMdFile({ metaOnly });
246
246
  } else if (context === 'claude-project') {
247
- if (!params.baseDir || !String(params.baseDir).trim()) {
248
- return { error: 'project path is required for claude-project context' };
249
- }
250
247
  readResult = readClaudeMdFile({ ...params, metaOnly });
251
248
  } else if (context === 'openclaw') {
252
249
  readResult = readOpenclawAgentsFile({ metaOnly });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexmate",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/web-ui/app.js CHANGED
@@ -231,6 +231,7 @@ document.addEventListener('DOMContentLoaded', () => {
231
231
  sessionTimelineLastAnchorY: 0,
232
232
  sessionTimelineLastDirection: 0,
233
233
  sessionTimelineEnabled: true,
234
+ sessionTimelineStyle: 'dots',
234
235
  sessionMessageRefMap: Object.create(null),
235
236
  sessionMessageRefBinderMap: Object.create(null),
236
237
  sessionPreviewScrollEl: null,
@@ -271,7 +272,7 @@ document.addEventListener('DOMContentLoaded', () => {
271
272
  healthCheckBatchFailed: 0,
272
273
  installPackageManager: 'npm',
273
274
  installCommandAction: 'install',
274
- installRegistryPreset: 'default',
275
+ installRegistryPreset: 'npmmirror',
275
276
  installRegistryCustom: '',
276
277
  installStatusTargets: null,
277
278
  appLatestVersion: '',
@@ -576,6 +577,10 @@ document.addEventListener('DOMContentLoaded', () => {
576
577
  this.shareCommandPrefix = this.normalizeShareCommandPrefix(localStorage.getItem('codexmateShareCommandPrefix'));
577
578
  this.sessionTrashEnabled = this.normalizeSessionTrashEnabled(localStorage.getItem('codexmateSessionTrashEnabled'));
578
579
  this.sessionTrashRetentionDays = this.normalizeSessionTrashRetentionDays(localStorage.getItem('codexmateSessionTrashRetentionDays'));
580
+ try {
581
+ var savedTimelineStyle = localStorage.getItem('codexmateSessionTimelineStyle');
582
+ this.sessionTimelineStyle = savedTimelineStyle === 'bar' ? 'bar' : 'dots';
583
+ } catch (_) {}
579
584
  this.configTemplateDiffConfirmEnabled = loadConfigTemplateDiffConfirmEnabledFromStorage(localStorage);
580
585
  try {
581
586
  var savedProjectPath = localStorage.getItem('codexmate_project_claude_md_path');
@@ -752,6 +757,9 @@ document.addEventListener('DOMContentLoaded', () => {
752
757
  watch: {
753
758
  mainTab(newTab) {
754
759
  if (newTab === 'prompts' && typeof this.loadPromptsContent === 'function') {
760
+ if (this.promptsSubTab === 'claude-project' && !this.projectPathOptions.length && !this.projectPathOptionsLoading && typeof this.loadProjectPathOptions === 'function') {
761
+ this.loadProjectPathOptions();
762
+ }
755
763
  this.loadPromptsContent();
756
764
  }
757
765
  },
@@ -261,6 +261,12 @@ export function createSessionComputed() {
261
261
  const matched = nodes.find(node => node.key === this.sessionTimelineActiveKey);
262
262
  return matched ? matched.title : '';
263
263
  },
264
+ sessionTimelineProgressPercent() {
265
+ if (!this.sessionTimelineActiveKey || !this.sessionTimelineNodes.length) return 0;
266
+ const index = this.sessionTimelineNodes.findIndex(node => node.key === this.sessionTimelineActiveKey);
267
+ if (index < 0) return 0;
268
+ return Math.round(((index + 1) / this.sessionTimelineNodes.length) * 100);
269
+ },
264
270
  sessionQueryPlaceholder() {
265
271
  if (this.isSessionQueryEnabled) {
266
272
  return typeof this.t === 'function'
@@ -727,6 +727,9 @@ export function createAgentsMethods(options = {}) {
727
727
  const rpcParams = {};
728
728
  if (subTab === 'claude-project') {
729
729
  action = 'get-claude-md-file';
730
+ if (!this.projectPathOptions.length && !this.projectPathOptionsLoading && typeof this.loadProjectPathOptions === 'function') {
731
+ this.loadProjectPathOptions();
732
+ }
730
733
  const projectPath = (this.projectClaudeMdPath || '').trim();
731
734
  if (projectPath) {
732
735
  rpcParams.baseDir = projectPath;
@@ -265,6 +265,12 @@ export function createSessionActionMethods(options = {}) {
265
265
  return true;
266
266
  },
267
267
 
268
+ normalizeSessionTimelineStyle(value) {
269
+ const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
270
+ if (normalized === 'bar') return 'bar';
271
+ return 'dots';
272
+ },
273
+
268
274
  normalizeConfigTemplateDiffConfirmEnabled(value) {
269
275
  return normalizeConfigTemplateDiffConfirmEnabled(value);
270
276
  },
@@ -277,6 +283,14 @@ export function createSessionActionMethods(options = {}) {
277
283
  } catch (_) {}
278
284
  },
279
285
 
286
+ setSessionTimelineStyle(style) {
287
+ const normalized = style === 'bar' ? 'bar' : 'dots';
288
+ this.sessionTimelineStyle = normalized;
289
+ try {
290
+ localStorage.setItem('codexmateSessionTimelineStyle', normalized);
291
+ } catch (_) {}
292
+ },
293
+
280
294
  setConfigTemplateDiffConfirmEnabled(value) {
281
295
  const enabled = this.normalizeConfigTemplateDiffConfirmEnabled(value);
282
296
  this.configTemplateDiffConfirmEnabled = enabled;
@@ -340,14 +354,18 @@ export function createSessionActionMethods(options = {}) {
340
354
  return;
341
355
  }
342
356
  const now = new Date();
343
- const year = String(now.getFullYear());
344
- const month = String(now.getMonth() + 1).padStart(2, '0');
345
- const day = String(now.getDate()).padStart(2, '0');
346
- const hour = String(now.getHours()).padStart(2, '0');
347
- const minute = String(now.getMinutes()).padStart(2, '0');
348
- const second = String(now.getSeconds()).padStart(2, '0');
349
- const fileName = `agent-${year}${month}${day}-${hour}${minute}${second}.txt`;
350
- this.downloadTextFile(fileName, text, 'text/plain;charset=utf-8');
357
+ const ts = [
358
+ String(now.getFullYear()),
359
+ String(now.getMonth() + 1).padStart(2, '0'),
360
+ String(now.getDate()).padStart(2, '0'),
361
+ '-',
362
+ String(now.getHours()).padStart(2, '0'),
363
+ String(now.getMinutes()).padStart(2, '0'),
364
+ String(now.getSeconds()).padStart(2, '0')
365
+ ].join('');
366
+ const prefix = this.promptsSubTab === 'claude-project' ? 'claude' : 'agents';
367
+ const fileName = `${prefix}-${ts}.md`;
368
+ this.downloadTextFile(fileName, text, 'text/markdown;charset=utf-8');
351
369
  this.showMessage(`已导出 ${fileName}`, 'success');
352
370
  },
353
371
 
@@ -1153,6 +1153,12 @@ const en = Object.freeze({
1153
1153
  'settings.templateConfirm.toggle': 'Preview diffs before apply (Confirm → Apply)',
1154
1154
  'settings.templateConfirm.hint': 'When enabled: show a diff preview first, then confirm writing.',
1155
1155
 
1156
+ 'settings.timeline.style.title': 'Timeline Style',
1157
+ 'settings.timeline.style.meta': 'Choose timeline navigation style in session preview',
1158
+ 'settings.timeline.style.dots': 'Dots',
1159
+ 'settings.timeline.style.bar': 'Bar',
1160
+ 'settings.timeline.style.hint': 'Dots for quick navigation, Bar for visual progress',
1161
+
1156
1162
  'settings.reset.title': 'Config reset',
1157
1163
  'settings.reset.meta': 'Proceed with caution',
1158
1164
  'settings.reset.hint': 'Backs up config.toml, then writes default config.',
@@ -1146,6 +1146,12 @@ const ja = Object.freeze({
1146
1146
  'settings.templateConfirm.toggle': 'テンプレート適用前に差分をプレビュー(2段階:確認 → 適用)',
1147
1147
  'settings.templateConfirm.hint': '有効時:先に差分プレビューを表示し、確認後に書き込みます。',
1148
1148
 
1149
+ 'settings.timeline.style.title': 'タイムラインスタイル',
1150
+ 'settings.timeline.style.meta': 'セッションプレビューのタイムラインナビゲーションスタイルを選択',
1151
+ 'settings.timeline.style.dots': 'ドット',
1152
+ 'settings.timeline.style.bar': 'バー',
1153
+ 'settings.timeline.style.hint': 'ドットは素早いナビゲーション、バーは進捗を直感的に表示',
1154
+
1149
1155
  'settings.reset.title': '設定リセット',
1150
1156
  'settings.reset.meta': '注意して操作してください',
1151
1157
  'settings.reset.hint': '先に config.toml をバックアップし、デフォルト設定を書き込みます。',
@@ -382,6 +382,13 @@ const vi = Object.freeze({
382
382
  // Sessions
383
383
  'sessions.preview.openLink': 'Mở liên kết',
384
384
 
385
+ // Settings
386
+ 'settings.timeline.style.title': 'Kiểu dòng thời gian',
387
+ 'settings.timeline.style.meta': 'Chọn kiểu điều hướng dòng thời gian trong xem trước phiên',
388
+ 'settings.timeline.style.dots': 'Chấm',
389
+ 'settings.timeline.style.bar': 'Thanh',
390
+ 'settings.timeline.style.hint': 'Chấm để điều hướng nhanh, Thanh để hiển thị tiến trình trực quan',
391
+
385
392
  // Diff / Agents hints
386
393
  'diff.hint.busy': 'Đang tạo diff hoặc áp dụng. Thao tác tạm thời bị vô hiệu hóa.',
387
394
  'diff.hint.failedBack': 'Xem trước diff thất bại. Hãy quay lại chỉnh sửa và thử lại.',
@@ -1156,6 +1156,12 @@ const zhTw = Object.freeze({
1156
1156
  'settings.templateConfirm.toggle': '應用模板前先預覽差異(兩步:確認 → 應用)',
1157
1157
  'settings.templateConfirm.hint': '開啟後:先展示差異預覽,再確認寫入。',
1158
1158
 
1159
+ 'settings.timeline.style.title': '時間線樣式',
1160
+ 'settings.timeline.style.meta': '選擇會話預覽中的時間線導航樣式',
1161
+ 'settings.timeline.style.dots': '點狀',
1162
+ 'settings.timeline.style.bar': '條狀',
1163
+ 'settings.timeline.style.hint': '點狀適合快速定位,條狀更直觀顯示進度',
1164
+
1159
1165
  'settings.reset.title': '設定重置',
1160
1166
  'settings.reset.meta': '謹慎操作',
1161
1167
  'settings.reset.hint': '會先備份 config.toml,再寫入預設設定。',
@@ -1156,6 +1156,12 @@ const zh = Object.freeze({
1156
1156
  'settings.templateConfirm.toggle': '应用模板前先预览差异(两步:确认 → 应用)',
1157
1157
  'settings.templateConfirm.hint': '开启后:先展示差异预览,再确认写入。',
1158
1158
 
1159
+ 'settings.timeline.style.title': '时间线样式',
1160
+ 'settings.timeline.style.meta': '选择会话预览中的时间线导航样式',
1161
+ 'settings.timeline.style.dots': '点状',
1162
+ 'settings.timeline.style.bar': '条状',
1163
+ 'settings.timeline.style.hint': '点状适合快速定位,条状更直观显示进度',
1164
+
1159
1165
  'settings.reset.title': '配置重置',
1160
1166
  'settings.reset.meta': '谨慎操作',
1161
1167
  'settings.reset.hint': '会先备份 config.toml,再写入默认配置。',
@@ -541,7 +541,7 @@
541
541
  </div>
542
542
  <div class="status-chip">
543
543
  <span class="label">{{ t('status.registry') }}</span>
544
- <span class="value">{{ installRegistryPreview || t('common.defaultOfficial') }}</span>
544
+ <span class="value">{{ installRegistryPreview || 'npmmirror' }}</span>
545
545
  </div>
546
546
  </div>
547
547
  <div class="status-strip status-strip-placeholder" v-else-if="!sessionStandalone" aria-hidden="true">
@@ -22,7 +22,6 @@
22
22
  <div class="docs-toolbar-card docs-toolbar-card-wide">
23
23
  <label class="form-label">{{ t('common.mirror') }}</label>
24
24
  <div class="install-action-tabs">
25
- <button type="button" class="btn-mini" :class="{ active: installRegistryPreset === 'default' }" @click="setInstallRegistryPreset('default')">{{ t('common.official') }}</button>
26
25
  <button type="button" class="btn-mini" :class="{ active: installRegistryPreset === 'npmmirror' }" @click="setInstallRegistryPreset('npmmirror')">npmmirror</button>
27
26
  <button type="button" class="btn-mini" :class="{ active: installRegistryPreset === 'tencent' }" @click="setInstallRegistryPreset('tencent')">{{ t('docs.registry.tencent') }}</button>
28
27
  <button type="button" class="btn-mini" :class="{ active: installRegistryPreset === 'custom' }" @click="setInstallRegistryPreset('custom')">{{ t('common.custom') }}</button>
@@ -52,7 +51,7 @@
52
51
  </div>
53
52
  <div class="docs-summary-item docs-summary-item-wide">
54
53
  <span class="docs-summary-label">{{ t('common.registry') }}</span>
55
- <strong class="docs-summary-value">{{ installRegistryPreview || t('common.defaultOfficial') }}</strong>
54
+ <strong class="docs-summary-value">{{ installRegistryPreview || 'npmmirror' }}</strong>
56
55
  </div>
57
56
  </div>
58
57
  </div>
@@ -45,33 +45,40 @@
45
45
  <button
46
46
  class="btn-mini"
47
47
  @click="exportAgentsContent"
48
- :disabled="agentsLoading">
49
- {{ t('modal.agents.export') }}
48
+ :disabled="agentsLoading"
49
+ :title="t('modal.agents.export')">
50
+ <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M8 11l4 4 4-4M4 17v2h16v-2"/></svg>
50
51
  </button>
51
52
  <button
52
53
  class="btn-mini"
53
54
  @click="copyAgentsContent"
54
- :disabled="agentsLoading">
55
- {{ t('modal.agents.copy') }}
55
+ :disabled="agentsLoading"
56
+ :title="t('modal.agents.copy')">
57
+ <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
56
58
  </button>
57
59
  <button
58
60
  class="btn-mini"
59
61
  @click="pasteAgentsContent"
60
- :disabled="agentsLoading || agentsSaving || agentsDiffVisible">
61
- {{ t('common.paste') }}
62
+ :disabled="agentsLoading || agentsSaving || agentsDiffVisible"
63
+ :title="t('common.paste')">
64
+ <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"/></svg>
62
65
  </button>
63
66
  </div>
64
67
  <div class="prompts-editor-group prompts-editor-group--workflow">
65
- <button class="btn-mini" @click="loadPromptsContent" :disabled="agentsSaving || agentsDiffLoading">{{ t('common.cancel') }}</button>
68
+ <button class="btn-mini" @click="loadPromptsContent" :disabled="agentsSaving || agentsDiffLoading" :title="t('common.cancel')">
69
+ <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
70
+ </button>
66
71
  <button
67
72
  v-if="agentsDiffVisible"
68
73
  class="btn-mini"
69
74
  @click="resetAgentsDiffState"
70
- :disabled="agentsSaving || agentsDiffLoading">
71
- {{ t('common.backToEdit') }}
75
+ :disabled="agentsSaving || agentsDiffLoading"
76
+ :title="t('common.backToEdit')">
77
+ <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
72
78
  </button>
73
- <button class="btn-mini btn-confirm-mini" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (!agentsDiffVisible && !hasAgentsContentChanged()) || (agentsDiffVisible && !agentsDiffHasChanges)">
74
- {{ agentsSaving ? (agentsDiffVisible ? t('common.saving') : t('common.previewing')) : (agentsDiffVisible ? t('common.save') : t('common.preview')) }}
79
+ <button class="btn-mini btn-confirm-mini" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (!agentsDiffVisible && !hasAgentsContentChanged()) || (agentsDiffVisible && !agentsDiffHasChanges)" :title="agentsDiffVisible ? t('common.save') : t('common.preview')">
80
+ <svg v-if="agentsDiffVisible" class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>
81
+ <svg v-else class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
75
82
  </button>
76
83
  </div>
77
84
  </div>
@@ -248,7 +248,7 @@
248
248
  :disabled="!activeSession || !canBuildStandaloneUrl(activeSession)"
249
249
  :title="t('sessions.preview.copyLink')"
250
250
  :aria-label="t('sessions.preview.copyLink')">
251
- <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="2.5" width="4" height="2" rx="1"/><path d="M4.5 4h7a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5"/><line x1="6" y1="7.5" x2="10" y2="7.5"/><line x1="6" y1="10" x2="10" y2="10"/></svg>
251
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6.5 9.5a3.5 3.5 0 0 0 5 0l2-2a3.5 3.5 0 0 0-5-5l-1 1"/><path d="M9.5 6.5a3.5 3.5 0 0 0-5 0l-2 2a3.5 3.5 0 0 0 5 5l1-1"/></svg>
252
252
  </button>
253
253
  <button
254
254
  class="btn-session-open"
@@ -265,7 +265,7 @@
265
265
  :disabled="!activeSession || !getSessionFilePath(activeSession)"
266
266
  :title="t('sessions.preview.copyPath')"
267
267
  :aria-label="t('sessions.preview.copyPath')">
268
- <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="2.5" width="4" height="2" rx="1"/><path d="M4.5 4h7a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5"/><line x1="6" y1="7.5" x2="10" y2="7.5"/><line x1="6" y1="10" x2="10" y2="10"/></svg>
268
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 5.5V12.5a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7.5a1 1 0 0 0-1-1H8L6.5 5H3a1 1 0 0 0-1 1z"/></svg>
269
269
  </button>
270
270
  </div>
271
271
  </div>
@@ -316,23 +316,50 @@
316
316
  </div>
317
317
  </div>
318
318
  </div>
319
- <aside v-if="sessionPreviewRenderEnabled && sessionTimelineNodes.length" class="session-timeline" :aria-label="t('sessions.timeline.aria')">
320
- <div class="session-timeline-track"></div>
321
- <button
322
- v-for="node in sessionTimelineNodes"
323
- :key="'timeline-' + node.key"
324
- v-memo="[sessionTimelineActiveKey === node.key, node.safePercent, node.title]"
325
- type="button"
326
- :class="['session-timeline-node', { active: sessionTimelineActiveKey === node.key }]"
327
- :aria-current="sessionTimelineActiveKey === node.key ? 'true' : null"
328
- :style="{ top: `${node.safePercent}%` }"
329
- :title="node.title"
330
- @click="jumpToSessionTimelineNode(node.key)">
331
- <span class="sr-only">{{ node.title }}</span>
332
- </button>
333
- <div class="session-timeline-current" v-if="sessionTimelineActiveTitle">
334
- {{ sessionTimelineActiveTitle }}
335
- </div>
319
+ <aside v-if="sessionPreviewRenderEnabled && sessionTimelineNodes.length" :class="['session-timeline', 'session-timeline-' + sessionTimelineStyle]" :aria-label="t('sessions.timeline.aria')">
320
+ <!-- 点状模式 -->
321
+ <template v-if="sessionTimelineStyle === 'dots'">
322
+ <div class="session-timeline-track"></div>
323
+ <button
324
+ v-for="node in sessionTimelineNodes"
325
+ :key="'timeline-' + node.key"
326
+ v-memo="[sessionTimelineActiveKey === node.key, node.safePercent, node.title]"
327
+ type="button"
328
+ :class="['session-timeline-node', { active: sessionTimelineActiveKey === node.key }]"
329
+ :aria-current="sessionTimelineActiveKey === node.key ? 'true' : null"
330
+ :style="{ top: `${node.safePercent}%` }"
331
+ :title="node.title"
332
+ @click="jumpToSessionTimelineNode(node.key)">
333
+ <span class="sr-only">{{ node.title }}</span>
334
+ </button>
335
+ <div class="session-timeline-current" v-if="sessionTimelineActiveTitle">
336
+ {{ sessionTimelineActiveTitle }}
337
+ </div>
338
+ </template>
339
+
340
+ <!-- 条状模式 -->
341
+ <template v-else>
342
+ <div class="session-timeline-bar-track">
343
+ <div class="session-timeline-bar-progress"
344
+ :style="{ height: sessionTimelineProgressPercent + '%' }">
345
+ </div>
346
+ </div>
347
+ <button
348
+ v-for="node in sessionTimelineNodes"
349
+ :key="'timeline-bar-' + node.key"
350
+ v-memo="[sessionTimelineActiveKey === node.key, node.safePercent, node.title]"
351
+ type="button"
352
+ :class="['session-timeline-bar-node', { active: sessionTimelineActiveKey === node.key }]"
353
+ :aria-current="sessionTimelineActiveKey === node.key ? 'true' : null"
354
+ :style="{ top: `${node.safePercent}%` }"
355
+ :title="node.title"
356
+ @click="jumpToSessionTimelineNode(node.key)">
357
+ <span class="sr-only">{{ node.title }}</span>
358
+ </button>
359
+ <div class="session-timeline-current" v-if="sessionTimelineActiveTitle">
360
+ {{ sessionTimelineActiveTitle }}
361
+ </div>
362
+ </template>
336
363
  </aside>
337
364
  </template>
338
365
 
@@ -114,6 +114,30 @@
114
114
  <span v-else>{{ t('settings.webhook.enable') }}</span>
115
115
  </button>
116
116
  </section>
117
+
118
+ <section class="settings-card" :aria-label="t('settings.timeline.style.title')">
119
+ <div class="settings-card-main">
120
+ <div class="settings-card-content">
121
+ <div class="settings-card-title">{{ t('settings.timeline.style.title') }}</div>
122
+ <p class="settings-card-desc">{{ t('settings.timeline.style.meta') }}</p>
123
+ <div class="settings-toggle-group">
124
+ <button
125
+ type="button"
126
+ :class="['toggle-btn', { active: sessionTimelineStyle === 'dots' }]"
127
+ @click="setSessionTimelineStyle('dots')">
128
+ {{ t('settings.timeline.style.dots') }}
129
+ </button>
130
+ <button
131
+ type="button"
132
+ :class="['toggle-btn', { active: sessionTimelineStyle === 'bar' }]"
133
+ @click="setSessionTimelineStyle('bar')">
134
+ {{ t('settings.timeline.style.bar') }}
135
+ </button>
136
+ </div>
137
+ <p class="settings-card-hint">{{ t('settings.timeline.style.hint') }}</p>
138
+ </div>
139
+ </div>
140
+ </section>
117
141
  </div>
118
142
  </div>
119
143
 
@@ -707,7 +707,7 @@ return function render(_ctx, _cache) {
707
707
  ]),
708
708
  _createElementVNode("div", { class: "status-chip" }, [
709
709
  _createElementVNode("span", { class: "label" }, _toDisplayString(_ctx.t('status.registry')), 1 /* TEXT */),
710
- _createElementVNode("span", { class: "value" }, _toDisplayString(_ctx.installRegistryPreview || _ctx.t('common.defaultOfficial')), 1 /* TEXT */)
710
+ _createElementVNode("span", { class: "value" }, _toDisplayString(_ctx.installRegistryPreview || 'npmmirror'), 1 /* TEXT */)
711
711
  ])
712
712
  ]))
713
713
  : (!_ctx.sessionStandalone)
@@ -3165,26 +3165,8 @@ return function render(_ctx, _cache) {
3165
3165
  "stroke-linecap": "round",
3166
3166
  "stroke-linejoin": "round"
3167
3167
  }, [
3168
- _createElementVNode("rect", {
3169
- x: "6",
3170
- y: "2.5",
3171
- width: "4",
3172
- height: "2",
3173
- rx: "1"
3174
- }),
3175
- _createElementVNode("path", { d: "M4.5 4h7a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5" }),
3176
- _createElementVNode("line", {
3177
- x1: "6",
3178
- y1: "7.5",
3179
- x2: "10",
3180
- y2: "7.5"
3181
- }),
3182
- _createElementVNode("line", {
3183
- x1: "6",
3184
- y1: "10",
3185
- x2: "10",
3186
- y2: "10"
3187
- })
3168
+ _createElementVNode("path", { d: "M6.5 9.5a3.5 3.5 0 0 0 5 0l2-2a3.5 3.5 0 0 0-5-5l-1 1" }),
3169
+ _createElementVNode("path", { d: "M9.5 6.5a3.5 3.5 0 0 0-5 0l-2 2a3.5 3.5 0 0 0 5 5l1-1" })
3188
3170
  ]))
3189
3171
  ], 8 /* PROPS */, ["onClick", "disabled", "title", "aria-label"]),
3190
3172
  _createElementVNode("button", {
@@ -3223,26 +3205,7 @@ return function render(_ctx, _cache) {
3223
3205
  "stroke-linecap": "round",
3224
3206
  "stroke-linejoin": "round"
3225
3207
  }, [
3226
- _createElementVNode("rect", {
3227
- x: "6",
3228
- y: "2.5",
3229
- width: "4",
3230
- height: "2",
3231
- rx: "1"
3232
- }),
3233
- _createElementVNode("path", { d: "M4.5 4h7a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5" }),
3234
- _createElementVNode("line", {
3235
- x1: "6",
3236
- y1: "7.5",
3237
- x2: "10",
3238
- y2: "7.5"
3239
- }),
3240
- _createElementVNode("line", {
3241
- x1: "6",
3242
- y1: "10",
3243
- x2: "10",
3244
- y2: "10"
3245
- })
3208
+ _createElementVNode("path", { d: "M2 5.5V12.5a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7.5a1 1 0 0 0-1-1H8L6.5 5H3a1 1 0 0 0-1 1z" })
3246
3209
  ]))
3247
3210
  ], 8 /* PROPS */, ["onClick", "disabled", "title", "aria-label"])
3248
3211
  ])
@@ -3320,34 +3283,70 @@ return function render(_ctx, _cache) {
3320
3283
  (_ctx.sessionPreviewRenderEnabled && _ctx.sessionTimelineNodes.length)
3321
3284
  ? (_openBlock(), _createElementBlock("aside", {
3322
3285
  key: 0,
3323
- class: "session-timeline",
3286
+ class: _normalizeClass(['session-timeline', 'session-timeline-' + _ctx.sessionTimelineStyle]),
3324
3287
  "aria-label": _ctx.t('sessions.timeline.aria')
3325
3288
  }, [
3326
- _createElementVNode("div", { class: "session-timeline-track" }),
3327
- (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sessionTimelineNodes, (node, __, ___, _cached) => {
3328
- const _memo = ([_ctx.sessionTimelineActiveKey === node.key, node.safePercent, node.title])
3329
- if (_cached && _cached.key === 'timeline-' + node.key && _isMemoSame(_cached, _memo)) return _cached
3330
- const _item = (_openBlock(), _createElementBlock("button", {
3331
- key: 'timeline-' + node.key,
3332
- type: "button",
3333
- class: _normalizeClass(['session-timeline-node', { active: _ctx.sessionTimelineActiveKey === node.key }]),
3334
- "aria-current": _ctx.sessionTimelineActiveKey === node.key ? 'true' : null,
3335
- style: _normalizeStyle({ top: `${node.safePercent}%` }),
3336
- title: node.title,
3337
- onClick: $event => (_ctx.jumpToSessionTimelineNode(node.key))
3338
- }, [
3339
- _createElementVNode("span", { class: "sr-only" }, _toDisplayString(node.title), 1 /* TEXT */)
3340
- ], 14 /* CLASS, STYLE, PROPS */, ["aria-current", "title", "onClick"]))
3341
- _item.memo = _memo
3342
- return _item
3343
- }, _cache, 4), 128 /* KEYED_FRAGMENT */)),
3344
- (_ctx.sessionTimelineActiveTitle)
3345
- ? (_openBlock(), _createElementBlock("div", {
3346
- key: 0,
3347
- class: "session-timeline-current"
3348
- }, _toDisplayString(_ctx.sessionTimelineActiveTitle), 1 /* TEXT */))
3349
- : _createCommentVNode("v-if", true)
3350
- ], 8 /* PROPS */, ["aria-label"]))
3289
+ _createCommentVNode(" 点状模式 "),
3290
+ (_ctx.sessionTimelineStyle === 'dots')
3291
+ ? (_openBlock(), _createElementBlock(_Fragment, { key: 0 }, [
3292
+ _createElementVNode("div", { class: "session-timeline-track" }),
3293
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sessionTimelineNodes, (node, __, ___, _cached) => {
3294
+ const _memo = ([_ctx.sessionTimelineActiveKey === node.key, node.safePercent, node.title])
3295
+ if (_cached && _cached.key === 'timeline-' + node.key && _isMemoSame(_cached, _memo)) return _cached
3296
+ const _item = (_openBlock(), _createElementBlock("button", {
3297
+ key: 'timeline-' + node.key,
3298
+ type: "button",
3299
+ class: _normalizeClass(['session-timeline-node', { active: _ctx.sessionTimelineActiveKey === node.key }]),
3300
+ "aria-current": _ctx.sessionTimelineActiveKey === node.key ? 'true' : null,
3301
+ style: _normalizeStyle({ top: `${node.safePercent}%` }),
3302
+ title: node.title,
3303
+ onClick: $event => (_ctx.jumpToSessionTimelineNode(node.key))
3304
+ }, [
3305
+ _createElementVNode("span", { class: "sr-only" }, _toDisplayString(node.title), 1 /* TEXT */)
3306
+ ], 14 /* CLASS, STYLE, PROPS */, ["aria-current", "title", "onClick"]))
3307
+ _item.memo = _memo
3308
+ return _item
3309
+ }, _cache, 4), 128 /* KEYED_FRAGMENT */)),
3310
+ (_ctx.sessionTimelineActiveTitle)
3311
+ ? (_openBlock(), _createElementBlock("div", {
3312
+ key: 0,
3313
+ class: "session-timeline-current"
3314
+ }, _toDisplayString(_ctx.sessionTimelineActiveTitle), 1 /* TEXT */))
3315
+ : _createCommentVNode("v-if", true)
3316
+ ], 64 /* STABLE_FRAGMENT */))
3317
+ : (_openBlock(), _createElementBlock(_Fragment, { key: 1 }, [
3318
+ _createCommentVNode(" 条状模式 "),
3319
+ _createElementVNode("div", { class: "session-timeline-bar-track" }, [
3320
+ _createElementVNode("div", {
3321
+ class: "session-timeline-bar-progress",
3322
+ style: _normalizeStyle({ height: _ctx.sessionTimelineProgressPercent + '%' })
3323
+ }, null, 4 /* STYLE */)
3324
+ ]),
3325
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sessionTimelineNodes, (node, __, ___, _cached) => {
3326
+ const _memo = ([_ctx.sessionTimelineActiveKey === node.key, node.safePercent, node.title])
3327
+ if (_cached && _cached.key === 'timeline-bar-' + node.key && _isMemoSame(_cached, _memo)) return _cached
3328
+ const _item = (_openBlock(), _createElementBlock("button", {
3329
+ key: 'timeline-bar-' + node.key,
3330
+ type: "button",
3331
+ class: _normalizeClass(['session-timeline-bar-node', { active: _ctx.sessionTimelineActiveKey === node.key }]),
3332
+ "aria-current": _ctx.sessionTimelineActiveKey === node.key ? 'true' : null,
3333
+ style: _normalizeStyle({ top: `${node.safePercent}%` }),
3334
+ title: node.title,
3335
+ onClick: $event => (_ctx.jumpToSessionTimelineNode(node.key))
3336
+ }, [
3337
+ _createElementVNode("span", { class: "sr-only" }, _toDisplayString(node.title), 1 /* TEXT */)
3338
+ ], 14 /* CLASS, STYLE, PROPS */, ["aria-current", "title", "onClick"]))
3339
+ _item.memo = _memo
3340
+ return _item
3341
+ }, _cache, 6), 128 /* KEYED_FRAGMENT */)),
3342
+ (_ctx.sessionTimelineActiveTitle)
3343
+ ? (_openBlock(), _createElementBlock("div", {
3344
+ key: 0,
3345
+ class: "session-timeline-current"
3346
+ }, _toDisplayString(_ctx.sessionTimelineActiveTitle), 1 /* TEXT */))
3347
+ : _createCommentVNode("v-if", true)
3348
+ ], 64 /* STABLE_FRAGMENT */))
3349
+ ], 10 /* CLASS, PROPS */, ["aria-label"]))
3351
3350
  : _createCommentVNode("v-if", true)
3352
3351
  ], 64 /* STABLE_FRAGMENT */))
3353
3352
  : (_openBlock(), _createElementBlock("div", {
@@ -4526,11 +4525,6 @@ return function render(_ctx, _cache) {
4526
4525
  _createElementVNode("div", { class: "docs-toolbar-card docs-toolbar-card-wide" }, [
4527
4526
  _createElementVNode("label", { class: "form-label" }, _toDisplayString(_ctx.t('common.mirror')), 1 /* TEXT */),
4528
4527
  _createElementVNode("div", { class: "install-action-tabs" }, [
4529
- _createElementVNode("button", {
4530
- type: "button",
4531
- class: _normalizeClass(["btn-mini", { active: _ctx.installRegistryPreset === 'default' }]),
4532
- onClick: $event => (_ctx.setInstallRegistryPreset('default'))
4533
- }, _toDisplayString(_ctx.t('common.official')), 11 /* TEXT, CLASS, PROPS */, ["onClick"]),
4534
4528
  _createElementVNode("button", {
4535
4529
  type: "button",
4536
4530
  class: _normalizeClass(["btn-mini", { active: _ctx.installRegistryPreset === 'npmmirror' }]),
@@ -4601,7 +4595,7 @@ return function render(_ctx, _cache) {
4601
4595
  ]),
4602
4596
  _createElementVNode("div", { class: "docs-summary-item docs-summary-item-wide" }, [
4603
4597
  _createElementVNode("span", { class: "docs-summary-label" }, _toDisplayString(_ctx.t('common.registry')), 1 /* TEXT */),
4604
- _createElementVNode("strong", { class: "docs-summary-value" }, _toDisplayString(_ctx.installRegistryPreview || _ctx.t('common.defaultOfficial')), 1 /* TEXT */)
4598
+ _createElementVNode("strong", { class: "docs-summary-value" }, _toDisplayString(_ctx.installRegistryPreview || 'npmmirror'), 1 /* TEXT */)
4605
4599
  ])
4606
4600
  ])
4607
4601
  ]),
@@ -4843,6 +4837,30 @@ return function render(_ctx, _cache) {
4843
4837
  ? (_openBlock(), _createElementBlock("span", { key: 0 }, _toDisplayString(_ctx.webhookConfig.url ? _ctx.t('settings.webhook.edit') : _ctx.t('settings.webhook.configure')), 1 /* TEXT */))
4844
4838
  : (_openBlock(), _createElementBlock("span", { key: 1 }, _toDisplayString(_ctx.t('settings.webhook.enable')), 1 /* TEXT */))
4845
4839
  ], 10 /* CLASS, PROPS */, ["onClick"])
4840
+ ], 8 /* PROPS */, ["aria-label"]),
4841
+ _createElementVNode("section", {
4842
+ class: "settings-card",
4843
+ "aria-label": _ctx.t('settings.timeline.style.title')
4844
+ }, [
4845
+ _createElementVNode("div", { class: "settings-card-main" }, [
4846
+ _createElementVNode("div", { class: "settings-card-content" }, [
4847
+ _createElementVNode("div", { class: "settings-card-title" }, _toDisplayString(_ctx.t('settings.timeline.style.title')), 1 /* TEXT */),
4848
+ _createElementVNode("p", { class: "settings-card-desc" }, _toDisplayString(_ctx.t('settings.timeline.style.meta')), 1 /* TEXT */),
4849
+ _createElementVNode("div", { class: "settings-toggle-group" }, [
4850
+ _createElementVNode("button", {
4851
+ type: "button",
4852
+ class: _normalizeClass(['toggle-btn', { active: _ctx.sessionTimelineStyle === 'dots' }]),
4853
+ onClick: $event => (_ctx.setSessionTimelineStyle('dots'))
4854
+ }, _toDisplayString(_ctx.t('settings.timeline.style.dots')), 11 /* TEXT, CLASS, PROPS */, ["onClick"]),
4855
+ _createElementVNode("button", {
4856
+ type: "button",
4857
+ class: _normalizeClass(['toggle-btn', { active: _ctx.sessionTimelineStyle === 'bar' }]),
4858
+ onClick: $event => (_ctx.setSessionTimelineStyle('bar'))
4859
+ }, _toDisplayString(_ctx.t('settings.timeline.style.bar')), 11 /* TEXT, CLASS, PROPS */, ["onClick"])
4860
+ ]),
4861
+ _createElementVNode("p", { class: "settings-card-hint" }, _toDisplayString(_ctx.t('settings.timeline.style.hint')), 1 /* TEXT */)
4862
+ ])
4863
+ ])
4846
4864
  ], 8 /* PROPS */, ["aria-label"])
4847
4865
  ])
4848
4866
  ], 512 /* NEED_PATCH */), [
@@ -6030,38 +6048,135 @@ return function render(_ctx, _cache) {
6030
6048
  _createElementVNode("button", {
6031
6049
  class: "btn-mini",
6032
6050
  onClick: _ctx.exportAgentsContent,
6033
- disabled: _ctx.agentsLoading
6034
- }, _toDisplayString(_ctx.t('modal.agents.export')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
6051
+ disabled: _ctx.agentsLoading,
6052
+ title: _ctx.t('modal.agents.export')
6053
+ }, [
6054
+ (_openBlock(), _createElementBlock("svg", {
6055
+ class: "btn-icon-sm",
6056
+ viewBox: "0 0 24 24",
6057
+ fill: "none",
6058
+ stroke: "currentColor",
6059
+ "stroke-width": "2"
6060
+ }, [
6061
+ _createElementVNode("path", { d: "M12 3v12M8 11l4 4 4-4M4 17v2h16v-2" })
6062
+ ]))
6063
+ ], 8 /* PROPS */, ["onClick", "disabled", "title"]),
6035
6064
  _createElementVNode("button", {
6036
6065
  class: "btn-mini",
6037
6066
  onClick: _ctx.copyAgentsContent,
6038
- disabled: _ctx.agentsLoading
6039
- }, _toDisplayString(_ctx.t('modal.agents.copy')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
6067
+ disabled: _ctx.agentsLoading,
6068
+ title: _ctx.t('modal.agents.copy')
6069
+ }, [
6070
+ (_openBlock(), _createElementBlock("svg", {
6071
+ class: "btn-icon-sm",
6072
+ viewBox: "0 0 24 24",
6073
+ fill: "none",
6074
+ stroke: "currentColor",
6075
+ "stroke-width": "2"
6076
+ }, [
6077
+ _createElementVNode("rect", {
6078
+ x: "9",
6079
+ y: "9",
6080
+ width: "13",
6081
+ height: "13",
6082
+ rx: "2"
6083
+ }),
6084
+ _createElementVNode("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
6085
+ ]))
6086
+ ], 8 /* PROPS */, ["onClick", "disabled", "title"]),
6040
6087
  _createElementVNode("button", {
6041
6088
  class: "btn-mini",
6042
6089
  onClick: _ctx.pasteAgentsContent,
6043
- disabled: _ctx.agentsLoading || _ctx.agentsSaving || _ctx.agentsDiffVisible
6044
- }, _toDisplayString(_ctx.t('common.paste')), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
6090
+ disabled: _ctx.agentsLoading || _ctx.agentsSaving || _ctx.agentsDiffVisible,
6091
+ title: _ctx.t('common.paste')
6092
+ }, [
6093
+ (_openBlock(), _createElementBlock("svg", {
6094
+ class: "btn-icon-sm",
6095
+ viewBox: "0 0 24 24",
6096
+ fill: "none",
6097
+ stroke: "currentColor",
6098
+ "stroke-width": "2"
6099
+ }, [
6100
+ _createElementVNode("rect", {
6101
+ x: "8",
6102
+ y: "2",
6103
+ width: "8",
6104
+ height: "4",
6105
+ rx: "1"
6106
+ }),
6107
+ _createElementVNode("path", { d: "M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2" })
6108
+ ]))
6109
+ ], 8 /* PROPS */, ["onClick", "disabled", "title"])
6045
6110
  ]),
6046
6111
  _createElementVNode("div", { class: "prompts-editor-group prompts-editor-group--workflow" }, [
6047
6112
  _createElementVNode("button", {
6048
6113
  class: "btn-mini",
6049
6114
  onClick: _ctx.loadPromptsContent,
6050
- disabled: _ctx.agentsSaving || _ctx.agentsDiffLoading
6051
- }, _toDisplayString(_ctx.t('common.cancel')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
6115
+ disabled: _ctx.agentsSaving || _ctx.agentsDiffLoading,
6116
+ title: _ctx.t('common.cancel')
6117
+ }, [
6118
+ (_openBlock(), _createElementBlock("svg", {
6119
+ class: "btn-icon-sm",
6120
+ viewBox: "0 0 24 24",
6121
+ fill: "none",
6122
+ stroke: "currentColor",
6123
+ "stroke-width": "2"
6124
+ }, [
6125
+ _createElementVNode("path", { d: "M18 6L6 18M6 6l12 12" })
6126
+ ]))
6127
+ ], 8 /* PROPS */, ["onClick", "disabled", "title"]),
6052
6128
  (_ctx.agentsDiffVisible)
6053
6129
  ? (_openBlock(), _createElementBlock("button", {
6054
6130
  key: 0,
6055
6131
  class: "btn-mini",
6056
6132
  onClick: _ctx.resetAgentsDiffState,
6057
- disabled: _ctx.agentsSaving || _ctx.agentsDiffLoading
6058
- }, _toDisplayString(_ctx.t('common.backToEdit')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]))
6133
+ disabled: _ctx.agentsSaving || _ctx.agentsDiffLoading,
6134
+ title: _ctx.t('common.backToEdit')
6135
+ }, [
6136
+ (_openBlock(), _createElementBlock("svg", {
6137
+ class: "btn-icon-sm",
6138
+ viewBox: "0 0 24 24",
6139
+ fill: "none",
6140
+ stroke: "currentColor",
6141
+ "stroke-width": "2"
6142
+ }, [
6143
+ _createElementVNode("path", { d: "M19 12H5M12 19l-7-7 7-7" })
6144
+ ]))
6145
+ ], 8 /* PROPS */, ["onClick", "disabled", "title"]))
6059
6146
  : _createCommentVNode("v-if", true),
6060
6147
  _createElementVNode("button", {
6061
6148
  class: "btn-mini btn-confirm-mini",
6062
6149
  onClick: _ctx.applyAgentsContent,
6063
- disabled: _ctx.agentsSaving || _ctx.agentsLoading || _ctx.agentsDiffLoading || (!_ctx.agentsDiffVisible && !_ctx.hasAgentsContentChanged()) || (_ctx.agentsDiffVisible && !_ctx.agentsDiffHasChanges)
6064
- }, _toDisplayString(_ctx.agentsSaving ? (_ctx.agentsDiffVisible ? _ctx.t('common.saving') : _ctx.t('common.previewing')) : (_ctx.agentsDiffVisible ? _ctx.t('common.save') : _ctx.t('common.preview'))), 9 /* TEXT, PROPS */, ["onClick", "disabled"])
6150
+ disabled: _ctx.agentsSaving || _ctx.agentsLoading || _ctx.agentsDiffLoading || (!_ctx.agentsDiffVisible && !_ctx.hasAgentsContentChanged()) || (_ctx.agentsDiffVisible && !_ctx.agentsDiffHasChanges),
6151
+ title: _ctx.agentsDiffVisible ? _ctx.t('common.save') : _ctx.t('common.preview')
6152
+ }, [
6153
+ (_ctx.agentsDiffVisible)
6154
+ ? (_openBlock(), _createElementBlock("svg", {
6155
+ key: 0,
6156
+ class: "btn-icon-sm",
6157
+ viewBox: "0 0 24 24",
6158
+ fill: "none",
6159
+ stroke: "currentColor",
6160
+ "stroke-width": "2"
6161
+ }, [
6162
+ _createElementVNode("path", { d: "M20 6L9 17l-5-5" })
6163
+ ]))
6164
+ : (_openBlock(), _createElementBlock("svg", {
6165
+ key: 1,
6166
+ class: "btn-icon-sm",
6167
+ viewBox: "0 0 24 24",
6168
+ fill: "none",
6169
+ stroke: "currentColor",
6170
+ "stroke-width": "2"
6171
+ }, [
6172
+ _createElementVNode("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
6173
+ _createElementVNode("circle", {
6174
+ cx: "12",
6175
+ cy: "12",
6176
+ r: "3"
6177
+ })
6178
+ ]))
6179
+ ], 8 /* PROPS */, ["onClick", "disabled", "title"])
6065
6180
  ])
6066
6181
  ])
6067
6182
  ]),
@@ -104,10 +104,12 @@
104
104
  display: flex;
105
105
  align-items: center;
106
106
  gap: 10px;
107
- padding: 10px 10px 10px 14px;
107
+ padding: 10px 14px;
108
108
  border: 1px solid var(--color-border-soft);
109
109
  border-radius: var(--radius-sm);
110
110
  background: var(--color-surface-alt);
111
+ overflow: hidden;
112
+ max-width: 100%;
111
113
  }
112
114
 
113
115
  .docs-command-box .install-command {
@@ -117,8 +119,9 @@
117
119
  border-radius: var(--radius-sm);
118
120
  background: var(--color-surface);
119
121
  border: 1px solid var(--color-border-soft);
120
- overflow-x: auto;
121
- white-space: nowrap;
122
+ white-space: normal;
123
+ word-break: break-all;
124
+ overflow-wrap: break-word;
122
125
  font-size: 13px;
123
126
  line-height: 1.5;
124
127
  font-family: var(--font-family-mono);
@@ -234,12 +237,8 @@
234
237
  grid-column: span 1;
235
238
  }
236
239
 
237
- .docs-command-box {
238
- flex-direction: column;
239
- align-items: stretch;
240
- }
241
-
242
- .docs-copy-btn {
243
- width: 100%;
240
+ .docs-install-row,
241
+ .docs-command-row {
242
+ overflow: hidden;
244
243
  }
245
244
  }
@@ -472,11 +472,12 @@
472
472
  content: "";
473
473
  position: absolute;
474
474
  left: 0;
475
- top: 10px;
476
- bottom: 10px;
477
- width: 3px;
475
+ top: 12px;
476
+ bottom: 12px;
477
+ width: 2px;
478
478
  border-radius: 999px;
479
479
  background: var(--color-border-soft);
480
+ opacity: 0.6;
480
481
  transition: background var(--transition-fast) var(--ease-spring);
481
482
  }
482
483
 
@@ -23,7 +23,7 @@
23
23
 
24
24
  .session-preview-header {
25
25
  padding: 14px var(--spacing-sm);
26
- border-bottom: 1px solid var(--color-border-soft);
26
+ border-bottom: none;
27
27
  display: flex;
28
28
  align-items: flex-start;
29
29
  justify-content: space-between;
@@ -31,8 +31,8 @@
31
31
  position: sticky;
32
32
  top: 0;
33
33
  z-index: 2;
34
- background: var(--color-surface-elevated);
35
- backdrop-filter: blur(12px);
34
+ background: rgba(255, 253, 252, 0.92);
35
+ backdrop-filter: blur(20px);
36
36
  }
37
37
 
38
38
  .session-preview-header > div:first-child {
@@ -179,13 +179,69 @@
179
179
  text-overflow: ellipsis;
180
180
  }
181
181
 
182
+ /* 条状时间线 */
183
+ .session-timeline-bar {
184
+ width: 8px;
185
+ padding: 0;
186
+ }
187
+
188
+ .session-timeline-bar-track {
189
+ position: absolute;
190
+ left: 50%;
191
+ top: 10px;
192
+ bottom: 32px;
193
+ width: 6px;
194
+ transform: translateX(-50%);
195
+ background: var(--color-border-soft);
196
+ border-radius: 3px;
197
+ overflow: hidden;
198
+ }
199
+
200
+ .session-timeline-bar-progress {
201
+ position: absolute;
202
+ top: 0;
203
+ left: 0;
204
+ right: 0;
205
+ width: 100%;
206
+ background: var(--color-brand);
207
+ border-radius: 3px;
208
+ transition: height var(--transition-fast) var(--ease-spring);
209
+ }
210
+
211
+ .session-timeline-bar-node {
212
+ position: absolute;
213
+ left: 50%;
214
+ width: 12px;
215
+ height: 12px;
216
+ border-radius: 50%;
217
+ border: 2px solid var(--color-border-strong);
218
+ background: var(--color-surface);
219
+ transform: translate(-50%, -50%);
220
+ cursor: pointer;
221
+ padding: 0;
222
+ transition: all var(--transition-fast) var(--ease-spring);
223
+ z-index: 2;
224
+ }
225
+
226
+ .session-timeline-bar-node:hover {
227
+ border-color: var(--color-brand);
228
+ background: var(--color-surface);
229
+ box-shadow: 0 0 0 3px rgba(210, 107, 90, 0.15);
230
+ }
231
+
232
+ .session-timeline-bar-node.active {
233
+ border-color: var(--color-brand);
234
+ background: var(--color-brand);
235
+ box-shadow: 0 0 0 3px rgba(210, 107, 90, 0.2);
236
+ }
237
+
182
238
  .session-msg {
183
239
  border-radius: var(--radius-md);
184
- padding: 10px 14px 10px 20px;
185
- border: 1px solid var(--color-border-soft);
240
+ padding: 12px 18px 12px 24px;
241
+ border: none;
186
242
  background: var(--color-surface);
187
243
  position: relative;
188
- box-shadow: var(--shadow-subtle);
244
+ box-shadow: none;
189
245
  contain: layout style paint;
190
246
  }
191
247
 
@@ -197,10 +253,10 @@
197
253
  .session-msg::before {
198
254
  content: "";
199
255
  position: absolute;
200
- left: 8px;
201
- top: 10px;
202
- bottom: 10px;
203
- width: 3px;
256
+ left: 6px;
257
+ top: 12px;
258
+ bottom: 12px;
259
+ width: 4px;
204
260
  border-radius: 999px;
205
261
  background: var(--color-border-soft);
206
262
  }
@@ -525,17 +525,19 @@
525
525
  }
526
526
 
527
527
  .session-match-snippets {
528
- margin-top: 4px;
528
+ margin-top: 6px;
529
+ padding-top: 6px;
530
+ border-top: 1px solid var(--color-border-soft);
529
531
  grid-column: 1 / -1;
530
532
  }
531
533
 
532
534
  .session-match-snippet {
533
535
  font-size: var(--font-size-caption);
534
536
  color: var(--color-text-tertiary);
535
- line-height: 1.35;
536
- padding: 3px 8px;
537
+ line-height: 1.4;
538
+ padding: 4px 8px;
537
539
  margin-bottom: 2px;
538
- background: var(--color-surface-alt);
540
+ background: rgba(0, 0, 0, 0.02);
539
541
  border-radius: var(--radius-sm);
540
542
  border-left: 2px solid var(--color-brand-light);
541
543
  overflow: hidden;
@@ -191,6 +191,53 @@
191
191
  color: var(--color-text-secondary);
192
192
  }
193
193
 
194
+ /* ---- 切换按钮组 ---- */
195
+ .settings-toggle-group {
196
+ display: flex;
197
+ gap: 4px;
198
+ padding: 3px;
199
+ border-radius: var(--radius-md);
200
+ background: var(--color-surface-alt);
201
+ border: 1px solid var(--color-border-soft);
202
+ margin: 12px 0;
203
+ }
204
+
205
+ .settings-toggle-group .toggle-btn {
206
+ display: inline-flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ padding: 8px 16px;
210
+ border-radius: var(--radius-sm);
211
+ border: none;
212
+ background: transparent;
213
+ color: var(--color-text-tertiary);
214
+ font-size: var(--font-size-secondary);
215
+ font-family: var(--font-family);
216
+ font-weight: var(--font-weight-secondary);
217
+ cursor: pointer;
218
+ transition: all var(--transition-fast) var(--ease-spring);
219
+ white-space: nowrap;
220
+ user-select: none;
221
+ -webkit-tap-highlight-color: transparent;
222
+ flex: 1;
223
+ }
224
+
225
+ .settings-toggle-group .toggle-btn:hover:not(:disabled) {
226
+ color: var(--color-text-secondary);
227
+ background: var(--color-surface);
228
+ }
229
+
230
+ .settings-toggle-group .toggle-btn:active:not(:disabled) {
231
+ transform: scale(0.97);
232
+ }
233
+
234
+ .settings-toggle-group .toggle-btn.active {
235
+ color: #fff;
236
+ background: var(--color-brand);
237
+ box-shadow: 0 1px 3px rgba(200, 121, 99, 0.35);
238
+ font-weight: var(--font-weight-primary);
239
+ }
240
+
194
241
  /* ---- 保留天数输入 ---- */
195
242
  .settings-retention {
196
243
  display: inline-flex;