codexmate 0.1.25 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/cli/config-bootstrap.js +2 -9
  2. package/cli/session-usage.js +10 -4
  3. package/cli.js +772 -225
  4. package/lib/cli-sessions.js +1 -1
  5. package/package.json +3 -3
  6. package/tools/dev/launch.js +147 -0
  7. package/web-ui/app.js +17 -3
  8. package/web-ui/index.html +12 -1
  9. package/web-ui/logic.prompts-token-count.mjs +49 -0
  10. package/web-ui/logic.sessions.mjs +30 -15
  11. package/web-ui/modules/app.computed.dashboard.mjs +1 -0
  12. package/web-ui/modules/app.computed.prompts.mjs +18 -0
  13. package/web-ui/modules/app.computed.session.mjs +3 -1
  14. package/web-ui/modules/app.methods.index.mjs +2 -0
  15. package/web-ui/modules/app.methods.session-actions.mjs +10 -4
  16. package/web-ui/modules/app.methods.session-browser.mjs +34 -45
  17. package/web-ui/modules/app.methods.session-timeline.mjs +1 -0
  18. package/web-ui/modules/app.methods.theme.mjs +38 -0
  19. package/web-ui/modules/app.methods.web-ui-preferences.mjs +9 -0
  20. package/web-ui/modules/i18n/locales/en.mjs +15 -2
  21. package/web-ui/modules/i18n/locales/ja.mjs +15 -2
  22. package/web-ui/modules/i18n/locales/vi.mjs +15 -2
  23. package/web-ui/modules/i18n/locales/zh-tw.mjs +15 -2
  24. package/web-ui/modules/i18n/locales/zh.mjs +15 -2
  25. package/web-ui/partials/index/layout-header.html +4 -2
  26. package/web-ui/partials/index/panel-prompts.html +14 -0
  27. package/web-ui/partials/index/panel-sessions.html +21 -6
  28. package/web-ui/partials/index/panel-settings.html +24 -0
  29. package/web-ui/res/web-ui-render.precompiled.js +115 -23
  30. package/web-ui/session-helpers.mjs +1 -1
  31. package/web-ui/styles/base-theme.css +25 -0
  32. package/web-ui/styles/controls-forms.css +1 -1
  33. package/web-ui/styles/modals-core.css +48 -0
  34. package/web-ui/styles/sessions-list.css +1 -1
  35. package/web-ui/styles/sessions-preview.css +56 -7
  36. package/web-ui/styles/sessions-toolbar-trash.css +2 -8
  37. package/web-ui/styles/sessions-usage.css +1 -1
  38. package/web-ui/styles/settings-panel.css +2 -2
  39. package/web-ui/styles/theme-dark-tokens.css +83 -0
  40. package/web-ui/styles/theme-dark.css +1355 -0
  41. package/web-ui/styles.css +1 -0
@@ -97,9 +97,6 @@
97
97
  <button class="btn-tool btn-tool-compact" type="button" @click="switchMainTab('dashboard')" :title="t('dashboard.doctor.title')">
98
98
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="btn-icon-sm"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
99
99
  </button>
100
- <button class="btn-tool btn-tool-compact" type="button" @click="copySessionsFilterShareUrl" :disabled="sessionsLoading">
101
- {{ t('sessions.filters.copyLink') }}
102
- </button>
103
100
  <button class="btn-tool btn-tool-compact" type="button" @click="clearSessionFilters" :disabled="sessionsLoading">
104
101
  {{ t('common.resetFilters') }}
105
102
  </button>
@@ -108,8 +105,8 @@
108
105
  <span>{{ t('sessions.batch.select') }}</span>
109
106
  </button>
110
107
  <template v-else>
111
- <button class="btn-tool btn-tool-compact" type="button" @click="toggleSelectAllVisibleForBatch" :disabled="sessionsLoading" :title="isAllVisibleBatchSelected() ? t('sessions.batch.unselectAll') : t('sessions.batch.selectAll')">
112
- {{ isAllVisibleBatchSelected() ? t('sessions.batch.unselectAll') : t('sessions.batch.selectAll') }}
108
+ <button class="btn-tool btn-tool-compact" type="button" @click="toggleSelectAllVisibleForBatch" :disabled="sessionsLoading" :title="isAllVisibleBatchSelected() ? t('sessions.batch.unselectAll') : t('sessions.batch.selectAllVisibleN', { count: getVisibleBatchSelectableCount() })">
109
+ {{ isAllVisibleBatchSelected() ? t('sessions.batch.unselectAll') : t('sessions.batch.selectAllVisibleN', { count: getVisibleBatchSelectableCount() }) }}
113
110
  </button>
114
111
  <button class="btn-tool btn-tool-compact btn-tool-batch-delete" type="button" @click="deleteSelectedSessions" :disabled="sessionsLoading || sessionDeletingSelected || getSelectedBatchCount() === 0" :title="getSelectedBatchCount() > 0 ? t('sessions.batch.deleteSelectedN', { count: getSelectedBatchCount() }) : t('sessions.batch.emptySelection')">
115
112
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M7 6V4a2 2 0 012-2h6a2 2 0 012 2v2M10 11v6M14 11v6"/><path d="M5 6l1 14a2 2 0 002 2h8a2 2 0 002-2l1-14"/></svg>
@@ -409,7 +406,7 @@
409
406
  <span class="session-msg-time">{{ msg.timestamp || '' }}</span>
410
407
  </div>
411
408
  </div>
412
- <div class="session-msg-content" v-html="highlightQueryText(msg.text) || ''"></div>
409
+ <div class="session-msg-content" v-html="msg.text || ''"></div>
413
410
  <button
414
411
  v-if="msg.normalizedRole === 'user'"
415
412
  type="button"
@@ -432,6 +429,24 @@
432
429
  </div>
433
430
  </div>
434
431
  </div>
432
+ <div class="session-preview-jump">
433
+ <button
434
+ type="button"
435
+ class="session-match-nav-btn"
436
+ @click="scrollSessionPreviewToTop"
437
+ :title="t('sessions.preview.scrollTop')"
438
+ :aria-label="t('sessions.preview.scrollTop')">
439
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 10 8 5 13 10"/></svg>
440
+ </button>
441
+ <button
442
+ type="button"
443
+ class="session-match-nav-btn"
444
+ @click="scrollSessionPreviewToBottom"
445
+ :title="t('sessions.preview.scrollBottom')"
446
+ :aria-label="t('sessions.preview.scrollBottom')">
447
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 8 11 13 6"/></svg>
448
+ </button>
449
+ </div>
435
450
  <aside v-if="sessionPreviewRenderEnabled && sessionTimelineNodes.length" :class="['session-timeline', 'session-timeline-' + sessionTimelineStyle]" :aria-label="t('sessions.timeline.aria')">
436
451
  <!-- 点状模式 -->
437
452
  <template v-if="sessionTimelineStyle === 'dots'">
@@ -61,6 +61,30 @@
61
61
  </div>
62
62
  </section>
63
63
 
64
+ <section class="settings-card" :aria-label="t('settings.theme.title')">
65
+ <div class="settings-card-main">
66
+ <div class="settings-card-content">
67
+ <div class="settings-card-title">{{ t('settings.theme.title') }}</div>
68
+ <p class="settings-card-desc">{{ t('settings.theme.meta') }}</p>
69
+ <div class="settings-toggle-group">
70
+ <button
71
+ type="button"
72
+ :class="['toggle-btn', { active: theme === 'light' }]"
73
+ @click="setTheme('light')">
74
+ {{ t('settings.theme.light') }}
75
+ </button>
76
+ <button
77
+ type="button"
78
+ :class="['toggle-btn', { active: theme === 'dark' }]"
79
+ @click="setTheme('dark')">
80
+ {{ t('settings.theme.dark') }}
81
+ </button>
82
+ </div>
83
+ <p class="settings-card-hint">{{ t('settings.theme.hint') }}</p>
84
+ </div>
85
+ </div>
86
+ </section>
87
+
64
88
  <section class="settings-card" :aria-label="t('settings.sharePrefix.title')">
65
89
  <div class="settings-card-main">
66
90
  <div class="settings-card-content">
@@ -412,7 +412,7 @@ return function render(_ctx, _cache) {
412
412
  _createElementVNode("div", { class: "side-item-title" }, _toDisplayString(_ctx.t('side.sessions.browser')), 1 /* TEXT */),
413
413
  _createElementVNode("div", { class: "side-item-meta" }, [
414
414
  _createElementVNode("span", null, _toDisplayString(_ctx.t('side.sessions.browser.meta')), 1 /* TEXT */),
415
- _createElementVNode("span", null, _toDisplayString(_ctx.t('sessions.sourceLabel', { value: (_ctx.sessionFilterSource === 'all' ? _ctx.t('sessions.source.all') : (_ctx.sessionFilterSource === 'claude' ? 'Claude Code' : (_ctx.sessionFilterSource === 'gemini' ? 'Gemini CLI' : (_ctx.sessionFilterSource === 'codebuddy' ? 'CodeBuddy Code' : 'Codex')))) })), 1 /* TEXT */)
415
+ _createElementVNode("span", null, _toDisplayString(_ctx.t('sessions.sourceLabel', { value: (_ctx.sessionFilterSource === 'all' ? _ctx.t('sessions.source.all') : (_ctx.sessionFilterSource === 'claude' ? 'Claude Code' : (_ctx.sessionFilterSource === 'gemini' ? 'Gemini CLI' : (_ctx.sessionFilterSource === 'codebuddy' ? 'CodeBuddy Code' : (_ctx.sessionFilterSource === 'workbuddy' ? 'WorkBuddy AI' : 'Codex'))))) })), 1 /* TEXT */)
416
416
  ])
417
417
  ], 42 /* CLASS, PROPS, NEED_HYDRATION */, ["aria-current", "onPointerdown", "onClick"]),
418
418
  _createElementVNode("button", {
@@ -727,7 +727,9 @@ return function render(_ctx, _cache) {
727
727
  ? _ctx.t('sessions.source.gemini')
728
728
  : (_ctx.sessionFilterSource === 'codebuddy'
729
729
  ? _ctx.t('sessions.source.codebuddy')
730
- : _ctx.t('sessions.source.codex')))))), 1 /* TEXT */)
730
+ : (_ctx.sessionFilterSource === 'workbuddy'
731
+ ? _ctx.t('sessions.source.workbuddy')
732
+ : _ctx.t('sessions.source.codex'))))))), 1 /* TEXT */)
731
733
  ]),
732
734
  _createElementVNode("div", { class: "status-chip" }, [
733
735
  _createElementVNode("span", { class: "label" }, _toDisplayString(_ctx.t('status.sessionCount')), 1 /* TEXT */),
@@ -4017,12 +4019,6 @@ return function render(_ctx, _cache) {
4017
4019
  _createElementVNode("path", { d: "M22 12h-4l-3 9L9 3l-3 9H2" })
4018
4020
  ]))
4019
4021
  ], 8 /* PROPS */, ["onClick", "title"]),
4020
- _createElementVNode("button", {
4021
- class: "btn-tool btn-tool-compact",
4022
- type: "button",
4023
- onClick: _ctx.copySessionsFilterShareUrl,
4024
- disabled: _ctx.sessionsLoading
4025
- }, _toDisplayString(_ctx.t('sessions.filters.copyLink')), 9 /* TEXT, PROPS */, ["onClick", "disabled"]),
4026
4022
  _createElementVNode("button", {
4027
4023
  class: "btn-tool btn-tool-compact",
4028
4024
  type: "button",
@@ -4056,8 +4052,8 @@ return function render(_ctx, _cache) {
4056
4052
  type: "button",
4057
4053
  onClick: _ctx.toggleSelectAllVisibleForBatch,
4058
4054
  disabled: _ctx.sessionsLoading,
4059
- title: _ctx.isAllVisibleBatchSelected() ? _ctx.t('sessions.batch.unselectAll') : _ctx.t('sessions.batch.selectAll')
4060
- }, _toDisplayString(_ctx.isAllVisibleBatchSelected() ? _ctx.t('sessions.batch.unselectAll') : _ctx.t('sessions.batch.selectAll')), 9 /* TEXT, PROPS */, ["onClick", "disabled", "title"]),
4055
+ title: _ctx.isAllVisibleBatchSelected() ? _ctx.t('sessions.batch.unselectAll') : _ctx.t('sessions.batch.selectAllVisibleN', { count: _ctx.getVisibleBatchSelectableCount() })
4056
+ }, _toDisplayString(_ctx.isAllVisibleBatchSelected() ? _ctx.t('sessions.batch.unselectAll') : _ctx.t('sessions.batch.selectAllVisibleN', { count: _ctx.getVisibleBatchSelectableCount() })), 9 /* TEXT, PROPS */, ["onClick", "disabled", "title"]),
4061
4057
  _createElementVNode("button", {
4062
4058
  class: "btn-tool btn-tool-compact btn-tool-batch-delete",
4063
4059
  type: "button",
@@ -4647,7 +4643,7 @@ return function render(_ctx, _cache) {
4647
4643
  ]),
4648
4644
  _createElementVNode("div", {
4649
4645
  class: "session-msg-content",
4650
- innerHTML: _ctx.highlightQueryText(msg.text) || ''
4646
+ innerHTML: msg.text || ''
4651
4647
  }, null, 8 /* PROPS */, ["innerHTML"]),
4652
4648
  (msg.normalizedRole === 'user')
4653
4649
  ? (_openBlock(), _createElementBlock("button", {
@@ -4696,6 +4692,44 @@ return function render(_ctx, _cache) {
4696
4692
  ])
4697
4693
  ]))
4698
4694
  ], 40 /* PROPS, NEED_HYDRATION */, ["onScroll"]),
4695
+ _createElementVNode("div", { class: "session-preview-jump" }, [
4696
+ _createElementVNode("button", {
4697
+ type: "button",
4698
+ class: "session-match-nav-btn",
4699
+ onClick: _ctx.scrollSessionPreviewToTop,
4700
+ title: _ctx.t('sessions.preview.scrollTop'),
4701
+ "aria-label": _ctx.t('sessions.preview.scrollTop')
4702
+ }, [
4703
+ (_openBlock(), _createElementBlock("svg", {
4704
+ viewBox: "0 0 16 16",
4705
+ fill: "none",
4706
+ stroke: "currentColor",
4707
+ "stroke-width": "1.8",
4708
+ "stroke-linecap": "round",
4709
+ "stroke-linejoin": "round"
4710
+ }, [
4711
+ _createElementVNode("polyline", { points: "3 10 8 5 13 10" })
4712
+ ]))
4713
+ ], 8 /* PROPS */, ["onClick", "title", "aria-label"]),
4714
+ _createElementVNode("button", {
4715
+ type: "button",
4716
+ class: "session-match-nav-btn",
4717
+ onClick: _ctx.scrollSessionPreviewToBottom,
4718
+ title: _ctx.t('sessions.preview.scrollBottom'),
4719
+ "aria-label": _ctx.t('sessions.preview.scrollBottom')
4720
+ }, [
4721
+ (_openBlock(), _createElementBlock("svg", {
4722
+ viewBox: "0 0 16 16",
4723
+ fill: "none",
4724
+ stroke: "currentColor",
4725
+ "stroke-width": "1.8",
4726
+ "stroke-linecap": "round",
4727
+ "stroke-linejoin": "round"
4728
+ }, [
4729
+ _createElementVNode("polyline", { points: "3 6 8 11 13 6" })
4730
+ ]))
4731
+ ], 8 /* PROPS */, ["onClick", "title", "aria-label"])
4732
+ ]),
4699
4733
  (_ctx.sessionPreviewRenderEnabled && _ctx.sessionTimelineNodes.length)
4700
4734
  ? (_openBlock(), _createElementBlock("aside", {
4701
4735
  key: 0,
@@ -5501,6 +5535,30 @@ return function render(_ctx, _cache) {
5501
5535
  ])
5502
5536
  ])
5503
5537
  ], 8 /* PROPS */, ["aria-label"]),
5538
+ _createElementVNode("section", {
5539
+ class: "settings-card",
5540
+ "aria-label": _ctx.t('settings.theme.title')
5541
+ }, [
5542
+ _createElementVNode("div", { class: "settings-card-main" }, [
5543
+ _createElementVNode("div", { class: "settings-card-content" }, [
5544
+ _createElementVNode("div", { class: "settings-card-title" }, _toDisplayString(_ctx.t('settings.theme.title')), 1 /* TEXT */),
5545
+ _createElementVNode("p", { class: "settings-card-desc" }, _toDisplayString(_ctx.t('settings.theme.meta')), 1 /* TEXT */),
5546
+ _createElementVNode("div", { class: "settings-toggle-group" }, [
5547
+ _createElementVNode("button", {
5548
+ type: "button",
5549
+ class: _normalizeClass(['toggle-btn', { active: _ctx.theme === 'light' }]),
5550
+ onClick: $event => (_ctx.setTheme('light'))
5551
+ }, _toDisplayString(_ctx.t('settings.theme.light')), 11 /* TEXT, CLASS, PROPS */, ["onClick"]),
5552
+ _createElementVNode("button", {
5553
+ type: "button",
5554
+ class: _normalizeClass(['toggle-btn', { active: _ctx.theme === 'dark' }]),
5555
+ onClick: $event => (_ctx.setTheme('dark'))
5556
+ }, _toDisplayString(_ctx.t('settings.theme.dark')), 11 /* TEXT, CLASS, PROPS */, ["onClick"])
5557
+ ]),
5558
+ _createElementVNode("p", { class: "settings-card-hint" }, _toDisplayString(_ctx.t('settings.theme.hint')), 1 /* TEXT */)
5559
+ ])
5560
+ ])
5561
+ ], 8 /* PROPS */, ["aria-label"]),
5504
5562
  _createElementVNode("section", {
5505
5563
  class: "settings-card",
5506
5564
  "aria-label": _ctx.t('settings.sharePrefix.title')
@@ -7034,6 +7092,12 @@ return function render(_ctx, _cache) {
7034
7092
  _createTextVNode(_toDisplayString(_ctx.agentsPath || _ctx.t('common.notLoaded')) + " ", 1 /* TEXT */),
7035
7093
  (_ctx.agentsPath)
7036
7094
  ? (_openBlock(), _createElementBlock("span", { key: 0 }, " (" + _toDisplayString(_ctx.agentsExists ? _ctx.t('common.exists') : _ctx.t('common.notExistsWillCreateOnSave')) + ") ", 1 /* TEXT */))
7095
+ : _createCommentVNode("v-if", true),
7096
+ (_ctx.agentsPath)
7097
+ ? (_openBlock(), _createElementBlock("span", {
7098
+ key: 1,
7099
+ class: "prompts-token-hint"
7100
+ }, "· " + _toDisplayString(_ctx.t('prompts.tokenCount.hint', { count: _ctx.agentsTokenCount })), 1 /* TEXT */))
7037
7101
  : _createCommentVNode("v-if", true)
7038
7102
  ]),
7039
7103
  _createElementVNode("div", { class: "prompts-editor-actions" }, [
@@ -7352,9 +7416,20 @@ return function render(_ctx, _cache) {
7352
7416
  _createElementVNode("div", { class: "form-group" }, [
7353
7417
  (_ctx.agentsDiffVisible)
7354
7418
  ? (_openBlock(), _createElementBlock("div", { key: 0 }, [
7355
- (!_ctx.agentsDiffLoading && !_ctx.agentsDiffError && !_ctx.agentsDiffTruncated && (_ctx.agentsDiffStats.added || _ctx.agentsDiffStats.removed))
7419
+ (!_ctx.agentsDiffLoading && !_ctx.agentsDiffError)
7356
7420
  ? (_openBlock(), _createElementBlock("div", {
7357
7421
  key: 0,
7422
+ class: "agents-diff-token-summary"
7423
+ }, [
7424
+ _createElementVNode("span", { class: "agents-diff-token-label" }, _toDisplayString(_ctx.t('prompts.tokenCount.diff', { before: _ctx.agentsDiffTokenStats.before, current: _ctx.agentsDiffTokenStats.current })), 1 /* TEXT */),
7425
+ _createElementVNode("span", {
7426
+ class: _normalizeClass(['agents-diff-token-delta', 'agents-diff-token-delta--' + _ctx.agentsDiffTokenStats.deltaKind])
7427
+ }, _toDisplayString(_ctx.agentsDiffTokenStats.deltaLabel), 3 /* TEXT, CLASS */)
7428
+ ]))
7429
+ : _createCommentVNode("v-if", true),
7430
+ (!_ctx.agentsDiffLoading && !_ctx.agentsDiffError && !_ctx.agentsDiffTruncated && (_ctx.agentsDiffStats.added || _ctx.agentsDiffStats.removed))
7431
+ ? (_openBlock(), _createElementBlock("div", {
7432
+ key: 1,
7358
7433
  class: "agents-diff-summary"
7359
7434
  }, [
7360
7435
  _createElementVNode("span", { class: "agents-diff-stat add" }, "+" + _toDisplayString(_ctx.agentsDiffStats.added), 1 /* TEXT */),
@@ -7363,26 +7438,26 @@ return function render(_ctx, _cache) {
7363
7438
  : _createCommentVNode("v-if", true),
7364
7439
  (_ctx.agentsDiffLoading)
7365
7440
  ? (_openBlock(), _createElementBlock("div", {
7366
- key: 1,
7441
+ key: 2,
7367
7442
  class: "state-message"
7368
7443
  }, _toDisplayString(_ctx.t('diff.generating')), 1 /* TEXT */))
7369
7444
  : (_ctx.agentsDiffError)
7370
7445
  ? (_openBlock(), _createElementBlock("div", {
7371
- key: 2,
7446
+ key: 3,
7372
7447
  class: "state-message error"
7373
7448
  }, _toDisplayString(_ctx.agentsDiffError), 1 /* TEXT */))
7374
7449
  : (_ctx.agentsDiffTruncated)
7375
7450
  ? (_openBlock(), _createElementBlock("div", {
7376
- key: 3,
7451
+ key: 4,
7377
7452
  class: "agents-diff-empty"
7378
7453
  }, _toDisplayString(_ctx.t('diff.tooLargeSkip')), 1 /* TEXT */))
7379
7454
  : (!_ctx.agentsDiffHasChanges)
7380
7455
  ? (_openBlock(), _createElementBlock("div", {
7381
- key: 4,
7456
+ key: 5,
7382
7457
  class: "agents-diff-empty"
7383
7458
  }, _toDisplayString(_ctx.t('diff.noChanges')), 1 /* TEXT */))
7384
7459
  : (_openBlock(), _createElementBlock("div", {
7385
- key: 5,
7460
+ key: 6,
7386
7461
  class: "prompts-diff-frame"
7387
7462
  }, [
7388
7463
  (_ctx.agentsChangeAxis.ticks.length)
@@ -7569,6 +7644,12 @@ return function render(_ctx, _cache) {
7569
7644
  _createTextVNode(_toDisplayString(_ctx.sysPromptPath || _ctx.t('common.notLoaded')) + " ", 1 /* TEXT */),
7570
7645
  (_ctx.sysPromptPath)
7571
7646
  ? (_openBlock(), _createElementBlock("span", { key: 0 }, " (" + _toDisplayString(_ctx.sysPromptExists ? _ctx.t('common.exists') : _ctx.t('common.notExistsWillCreateOnSave')) + ") ", 1 /* TEXT */))
7647
+ : _createCommentVNode("v-if", true),
7648
+ (_ctx.sysPromptPath)
7649
+ ? (_openBlock(), _createElementBlock("span", {
7650
+ key: 1,
7651
+ class: "prompts-token-hint"
7652
+ }, "· " + _toDisplayString(_ctx.t('prompts.tokenCount.hint', { count: _ctx.sysPromptTokenCount })), 1 /* TEXT */))
7572
7653
  : _createCommentVNode("v-if", true)
7573
7654
  ]),
7574
7655
  _createElementVNode("div", { class: "prompts-editor-actions" }, [
@@ -7793,9 +7874,20 @@ return function render(_ctx, _cache) {
7793
7874
  _createElementVNode("div", { class: "form-group" }, [
7794
7875
  (_ctx.sysPromptDiffVisible)
7795
7876
  ? (_openBlock(), _createElementBlock("div", { key: 0 }, [
7796
- (!_ctx.sysPromptDiffLoading && !_ctx.sysPromptDiffError && !_ctx.sysPromptDiffTruncated && (_ctx.sysPromptDiffStats.added || _ctx.sysPromptDiffStats.removed))
7877
+ (!_ctx.sysPromptDiffLoading && !_ctx.sysPromptDiffError)
7797
7878
  ? (_openBlock(), _createElementBlock("div", {
7798
7879
  key: 0,
7880
+ class: "agents-diff-token-summary"
7881
+ }, [
7882
+ _createElementVNode("span", { class: "agents-diff-token-label" }, _toDisplayString(_ctx.t('prompts.tokenCount.diff', { before: _ctx.sysPromptDiffTokenStats.before, current: _ctx.sysPromptDiffTokenStats.current })), 1 /* TEXT */),
7883
+ _createElementVNode("span", {
7884
+ class: _normalizeClass(['agents-diff-token-delta', 'agents-diff-token-delta--' + _ctx.sysPromptDiffTokenStats.deltaKind])
7885
+ }, _toDisplayString(_ctx.sysPromptDiffTokenStats.deltaLabel), 3 /* TEXT, CLASS */)
7886
+ ]))
7887
+ : _createCommentVNode("v-if", true),
7888
+ (!_ctx.sysPromptDiffLoading && !_ctx.sysPromptDiffError && !_ctx.sysPromptDiffTruncated && (_ctx.sysPromptDiffStats.added || _ctx.sysPromptDiffStats.removed))
7889
+ ? (_openBlock(), _createElementBlock("div", {
7890
+ key: 1,
7799
7891
  class: "agents-diff-summary"
7800
7892
  }, [
7801
7893
  _createElementVNode("span", { class: "agents-diff-stat add" }, "+" + _toDisplayString(_ctx.sysPromptDiffStats.added), 1 /* TEXT */),
@@ -7804,26 +7896,26 @@ return function render(_ctx, _cache) {
7804
7896
  : _createCommentVNode("v-if", true),
7805
7897
  (_ctx.sysPromptDiffLoading)
7806
7898
  ? (_openBlock(), _createElementBlock("div", {
7807
- key: 1,
7899
+ key: 2,
7808
7900
  class: "state-message"
7809
7901
  }, _toDisplayString(_ctx.t('diff.generating')), 1 /* TEXT */))
7810
7902
  : (_ctx.sysPromptDiffError)
7811
7903
  ? (_openBlock(), _createElementBlock("div", {
7812
- key: 2,
7904
+ key: 3,
7813
7905
  class: "state-message error"
7814
7906
  }, _toDisplayString(_ctx.sysPromptDiffError), 1 /* TEXT */))
7815
7907
  : (_ctx.sysPromptDiffTruncated)
7816
7908
  ? (_openBlock(), _createElementBlock("div", {
7817
- key: 3,
7909
+ key: 4,
7818
7910
  class: "agents-diff-empty"
7819
7911
  }, _toDisplayString(_ctx.t('diff.tooLargeSkip')), 1 /* TEXT */))
7820
7912
  : (!_ctx.sysPromptDiffHasChangesValue)
7821
7913
  ? (_openBlock(), _createElementBlock("div", {
7822
- key: 4,
7914
+ key: 5,
7823
7915
  class: "agents-diff-empty"
7824
7916
  }, _toDisplayString(_ctx.t('diff.noChanges')), 1 /* TEXT */))
7825
7917
  : (_openBlock(), _createElementBlock("div", {
7826
- key: 5,
7918
+ key: 6,
7827
7919
  class: "prompts-diff-frame"
7828
7920
  }, [
7829
7921
  (_ctx.sysChangeAxis.ticks.length)
@@ -383,7 +383,7 @@ export async function loadActiveSessionDetail(api, options = {}) {
383
383
  sessionId: this.activeSession.sessionId,
384
384
  filePath: this.activeSession.filePath,
385
385
  messageLimit,
386
- preview
386
+ preview,
387
387
  });
388
388
 
389
389
  if (requestSeq !== this.sessionDetailRequestSeq) {
@@ -15,6 +15,7 @@
15
15
  --color-surface-alt: #F7EFE8;
16
16
  --color-surface-elevated: #FFFFFF;
17
17
  --color-surface-tint: rgba(255, 253, 250, 0.86);
18
+ --color-surface-chip: rgba(255, 255, 255, 0.76);
18
19
  --color-text-primary: #241F1C;
19
20
  --color-text-secondary: #5A504A;
20
21
  --color-text-tertiary: #82746A;
@@ -289,3 +290,27 @@ body::before {
289
290
  radial-gradient(circle at 86% 18%, rgba(255, 232, 206, 0.36), rgba(255, 232, 206, 0) 30%);
290
291
  z-index: 0;
291
292
  }
293
+
294
+ /* Site-wide custom scrollbars (class-scoped scrollbar-width:none rules keep priority via specificity) */
295
+ * {
296
+ scrollbar-width: thin;
297
+ scrollbar-color: rgba(137, 111, 94, 0.35) transparent;
298
+ }
299
+
300
+ ::-webkit-scrollbar {
301
+ width: 8px;
302
+ height: 8px;
303
+ }
304
+
305
+ ::-webkit-scrollbar-track {
306
+ background: transparent;
307
+ }
308
+
309
+ ::-webkit-scrollbar-thumb {
310
+ background: rgba(137, 111, 94, 0.35);
311
+ border-radius: 999px;
312
+ }
313
+
314
+ ::-webkit-scrollbar-thumb:hover {
315
+ background: rgba(137, 111, 94, 0.55);
316
+ }
@@ -157,7 +157,7 @@
157
157
  line-height: 1 !important;
158
158
  border-radius: var(--radius-sm) !important;
159
159
  border: 1px solid var(--color-border-soft) !important;
160
- background: rgba(255, 255, 255, 0.76) !important;
160
+ background: var(--color-surface-chip) !important;
161
161
  font-size: 12px !important;
162
162
  font-weight: var(--font-weight-secondary) !important;
163
163
  color: var(--color-text-secondary) !important;
@@ -456,6 +456,54 @@
456
456
  font-weight: var(--font-weight-secondary);
457
457
  }
458
458
 
459
+ .agents-diff-token-summary {
460
+ display: flex;
461
+ align-items: center;
462
+ gap: 8px;
463
+ margin-bottom: 8px;
464
+ font-size: var(--font-size-caption);
465
+ color: var(--color-text-tertiary);
466
+ }
467
+
468
+ .agents-diff-token-label {
469
+ display: inline-flex;
470
+ align-items: center;
471
+ }
472
+
473
+ .agents-diff-token-delta {
474
+ display: inline-flex;
475
+ align-items: center;
476
+ padding: 2px 8px;
477
+ border-radius: 999px;
478
+ border: 1px solid transparent;
479
+ font-weight: var(--font-weight-secondary);
480
+ font-variant-numeric: tabular-nums;
481
+ }
482
+
483
+ .agents-diff-token-delta--add {
484
+ color: #2b6a3b;
485
+ background: rgba(57, 181, 97, 0.12);
486
+ border-color: rgba(57, 181, 97, 0.2);
487
+ }
488
+
489
+ .agents-diff-token-delta--del {
490
+ color: #8a2f36;
491
+ background: rgba(220, 95, 108, 0.12);
492
+ border-color: rgba(220, 95, 108, 0.2);
493
+ }
494
+
495
+ .agents-diff-token-delta--same {
496
+ color: var(--color-text-tertiary);
497
+ background: rgba(127, 127, 127, 0.1);
498
+ border-color: rgba(127, 127, 127, 0.2);
499
+ }
500
+
501
+ .prompts-token-hint {
502
+ margin-left: 4px;
503
+ color: var(--color-text-tertiary);
504
+ font-variant-numeric: tabular-nums;
505
+ }
506
+
459
507
  .agents-diff-summary {
460
508
  display: flex;
461
509
  align-items: center;
@@ -375,7 +375,7 @@
375
375
  grid-template-columns: minmax(260px, 360px) minmax(0, 1fr);
376
376
  gap: var(--spacing-md);
377
377
  align-items: start;
378
- height: min(72vh, 760px);
378
+ height: min(760px, calc(100dvh - 340px));
379
379
  min-height: 360px;
380
380
  }
381
381
 
@@ -632,13 +632,6 @@
632
632
 
633
633
  .session-source-pills {
634
634
  max-width: 100%;
635
- overflow-x: auto;
636
- flex-wrap: nowrap;
637
- scrollbar-width: none;
638
- }
639
-
640
- .session-source-pills::-webkit-scrollbar {
641
- display: none;
642
635
  }
643
636
 
644
637
  .session-path-select,
@@ -709,3 +702,59 @@
709
702
  transform: none;
710
703
  box-shadow: none;
711
704
  }
705
+
706
+
707
+ .session-match-nav-btn {
708
+ display: inline-flex;
709
+ align-items: center;
710
+ justify-content: center;
711
+ width: 28px;
712
+ height: 28px;
713
+ padding: 0;
714
+ border: 1px solid var(--border-subtle, rgba(0, 0, 0, 0.12));
715
+ border-radius: var(--radius-sm, 6px);
716
+ background: transparent;
717
+ color: var(--text-secondary, inherit);
718
+ cursor: pointer;
719
+ }
720
+
721
+ .session-match-nav-btn svg {
722
+ width: 14px;
723
+ height: 14px;
724
+ }
725
+
726
+ .session-match-nav-btn:hover {
727
+ background: var(--bg-hover, rgba(0, 0, 0, 0.06));
728
+ }
729
+
730
+ .session-match-nav-btn[disabled] {
731
+ opacity: 0.5;
732
+ cursor: not-allowed;
733
+ }
734
+
735
+ /* Session preview jump buttons (floating bottom-right, persistent top/bottom pair) */
736
+ .session-preview-jump {
737
+ position: fixed;
738
+ right: 12px;
739
+ bottom: calc(16px + env(safe-area-inset-bottom, 0px));
740
+ z-index: 5;
741
+ display: flex;
742
+ flex-direction: column;
743
+ align-items: center;
744
+ gap: 8px;
745
+ pointer-events: none;
746
+ }
747
+
748
+ .session-preview-jump .session-match-nav-btn {
749
+ pointer-events: auto;
750
+ width: 28px;
751
+ height: 28px;
752
+ border-radius: 50%;
753
+ background: var(--color-surface-elevated, rgba(255, 255, 255, 0.92));
754
+ box-shadow: var(--shadow-float, 0 4px 16px rgba(0, 0, 0, 0.16));
755
+ }
756
+
757
+ .session-preview-jump .session-match-nav-btn svg {
758
+ width: 14px;
759
+ height: 14px;
760
+ }
@@ -151,6 +151,7 @@
151
151
 
152
152
  .session-source-pills {
153
153
  display: flex;
154
+ flex-wrap: wrap;
154
155
  gap: 3px;
155
156
  padding: 2px;
156
157
  border-radius: var(--radius-md);
@@ -178,7 +179,7 @@
178
179
  -webkit-tap-highlight-color: transparent;
179
180
  }
180
181
 
181
- .session-source-pill:hover:not(:disabled) {
182
+ .session-source-pill:hover:not(:disabled):not(.active) {
182
183
  color: var(--color-text-secondary);
183
184
  background: var(--color-surface);
184
185
  }
@@ -611,10 +612,3 @@
611
612
  max-width: 100%;
612
613
  }
613
614
 
614
- /* ===== Session preview match highlight ===== */
615
- .session-msg-content mark {
616
- background: rgba(200, 121, 99, 0.18);
617
- color: var(--color-brand-dark);
618
- border-radius: 2px;
619
- padding: 0 1px;
620
- }
@@ -46,7 +46,7 @@
46
46
  -webkit-tap-highlight-color: transparent;
47
47
  }
48
48
 
49
- .usage-range-btn:hover:not(:disabled) {
49
+ .usage-range-btn:hover:not(:disabled):not(.active) {
50
50
  color: var(--color-text-secondary);
51
51
  background: rgba(200, 121, 99, 0.08);
52
52
  }
@@ -171,7 +171,7 @@
171
171
  left: 3px;
172
172
  width: 20px;
173
173
  height: 20px;
174
- background: white;
174
+ background: #fff;
175
175
  border-radius: 50%;
176
176
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
177
177
  transition: all 0.25s cubic-bezier(0.25, 1, 0.5, 1);
@@ -229,7 +229,7 @@
229
229
  flex: 1;
230
230
  }
231
231
 
232
- .settings-toggle-group .toggle-btn:hover:not(:disabled) {
232
+ .settings-toggle-group .toggle-btn:hover:not(:disabled):not(.active) {
233
233
  color: var(--color-text-secondary);
234
234
  background: var(--color-surface);
235
235
  }