codexmate 0.1.22 → 0.1.23

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/README.md CHANGED
@@ -26,16 +26,6 @@
26
26
 
27
27
  <br />
28
28
 
29
- <p>
30
- <img src="site/.vitepress/public/images/readme/config-codex.png" alt="Codex Mate Codex provider configuration" width="100%" />
31
- </p>
32
- <p>
33
- <img src="site/.vitepress/public/images/readme/config-opencode.png" alt="Codex Mate OpenCode provider configuration" width="100%" />
34
- </p>
35
- <p>
36
- <img src="site/.vitepress/public/images/readme/sessions.png" alt="Codex Mate unified session browser" width="100%" />
37
- </p>
38
-
39
29
  </div>
40
30
 
41
31
  ---
package/README.vi.md CHANGED
@@ -26,8 +26,6 @@
26
26
 
27
27
  <br />
28
28
 
29
- <img src="site/.vitepress/public/images/readme-hero.png" alt="Giao diện Codex Mate" width="100%" />
30
-
31
29
  </div>
32
30
 
33
31
  ---
package/README.zh.md CHANGED
@@ -25,16 +25,6 @@
25
25
 
26
26
  <br />
27
27
 
28
- <p>
29
- <img src="site/.vitepress/public/images/readme/config-codex.png" alt="Codex Mate Codex Provider 配置" width="100%" />
30
- </p>
31
- <p>
32
- <img src="site/.vitepress/public/images/readme/config-opencode.png" alt="Codex Mate OpenCode Provider 配置" width="100%" />
33
- </p>
34
- <p>
35
- <img src="site/.vitepress/public/images/readme/sessions.png" alt="Codex Mate 统一会话浏览器" width="100%" />
36
- </p>
37
-
38
28
  </div>
39
29
 
40
30
  ---
package/cli.js CHANGED
@@ -1076,6 +1076,7 @@ function normalizeWebUiPreferences(value = {}) {
1076
1076
  configTemplateDiffConfirmEnabled: normalizeBooleanPreference(source.configTemplateDiffConfirmEnabled, true),
1077
1077
  sessionsUsageTimeRange: normalizeUsageTimeRangePreference(source.sessionsUsageTimeRange),
1078
1078
  promptsSubTab: normalizePromptsSubTabPreference(source.promptsSubTab),
1079
+ promptsPreviewCollapsed: normalizeBooleanPreference(source.promptsPreviewCollapsed, true),
1079
1080
  sysPromptScope: normalizeSysPromptScopePreference(source.sysPromptScope),
1080
1081
  sysPromptMode: normalizeSysPromptModePreference(source.sysPromptMode),
1081
1082
  projectClaudeMdPath: typeof source.projectClaudeMdPath === 'string' ? source.projectClaudeMdPath : '',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codexmate",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/web-ui/app.js CHANGED
@@ -103,7 +103,9 @@ document.addEventListener('DOMContentLoaded', () => {
103
103
  showAgentsModal: false,
104
104
  promptsSubTab: 'codex',
105
105
  promptsPreviewEnabled: true,
106
- promptsPreviewCollapsed: false,
106
+ promptsPreviewCollapsed: true,
107
+ agentsChangeAxis: { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 },
108
+ sysChangeAxis: { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 },
107
109
  promptsMobileView: 'edit',
108
110
  promptsPreviewLibsMissing: false,
109
111
  agentsHighlightHtml: '',
@@ -826,6 +828,24 @@ document.addEventListener('DOMContentLoaded', () => {
826
828
  sysPromptContent() {
827
829
  if (typeof this.schedulePromptsEditorRefresh === 'function') this.schedulePromptsEditorRefresh('sys');
828
830
  },
831
+ agentsOriginalContent() {
832
+ if (typeof this.schedulePromptsChangeAxis === 'function') this.schedulePromptsChangeAxis('agents');
833
+ },
834
+ sysPromptOriginalContent() {
835
+ if (typeof this.schedulePromptsChangeAxis === 'function') this.schedulePromptsChangeAxis('sys');
836
+ },
837
+ agentsDiffVisible() {
838
+ if (typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('agents');
839
+ },
840
+ sysPromptDiffVisible() {
841
+ if (typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('sys');
842
+ },
843
+ agentsDiffLines() {
844
+ if (this.agentsDiffVisible && typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('agents');
845
+ },
846
+ sysPromptDiffLines() {
847
+ if (this.sysPromptDiffVisible && typeof this.refreshPromptsChangeAxis === 'function') this.refreshPromptsChangeAxis('sys');
848
+ },
829
849
  projectClaudeMdPath(newPath) {
830
850
  if (typeof this.persistWebUiPreferences === 'function') {
831
851
  this.persistWebUiPreferences({ projectClaudeMdPath: newPath || '' });
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Prompts-panel change axis: aggregates the line-level diff between the
3
+ * saved original content and the current editor content (or between the
4
+ * rendered diff rows) into clickable tick positions. Pure functions only.
5
+ *
6
+ * The diff engine itself is reused from logic.agents-diff.mjs (LCS for
7
+ * small inputs, window-sync for large ones) so hunk line numbers stay
8
+ * consistent with the red/green preview flow.
9
+ */
10
+
11
+ import { buildLineDiff } from './logic.agents-diff.mjs';
12
+
13
+ const AXIS_MAX_TICKS = 200;
14
+ const AXIS_MIN_TICK_HEIGHT_PERCENT = 1.2;
15
+
16
+ function isChangedDiffType(type) {
17
+ return type === 'add' || type === 'del';
18
+ }
19
+
20
+ function clampAxisPercent(value) {
21
+ if (!Number.isFinite(value) || value < 0) return 0;
22
+ if (value > 100) return 100;
23
+ return value;
24
+ }
25
+
26
+ function tickKindForRun(added, removed) {
27
+ if (added && removed) return 'mixed';
28
+ return added ? 'add' : 'del';
29
+ }
30
+
31
+ function buildTickFromRun(run, positioning) {
32
+ const height = clampAxisPercent(Math.max(
33
+ AXIS_MIN_TICK_HEIGHT_PERCENT,
34
+ (Math.max(run.added, run.removed) / Math.max(1, positioning.total)) * 100
35
+ ));
36
+ const top = Math.min(clampAxisPercent((run.anchor / Math.max(1, positioning.total)) * 100), 100 - height);
37
+ return {
38
+ kind: tickKindForRun(run.added, run.removed),
39
+ startLine: run.startLine,
40
+ endLine: run.startLine + Math.max(run.added, run.removed) - 1,
41
+ added: run.added,
42
+ removed: run.removed,
43
+ top,
44
+ height,
45
+ rowIndex: typeof run.rowIndex === 'number' ? run.rowIndex : null
46
+ };
47
+ }
48
+
49
+ function capAxisTicks(runs, positioning) {
50
+ const truncated = runs.length > AXIS_MAX_TICKS;
51
+ const visibleRuns = truncated ? runs.slice(0, AXIS_MAX_TICKS) : runs;
52
+ return {
53
+ ticks: visibleRuns.map((run) => buildTickFromRun(run, positioning)),
54
+ truncated
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Build change-axis ticks for the editor view.
60
+ *
61
+ * @param {string} baseText saved original content
62
+ * @param {string} currentText current editor content
63
+ * @returns {{ ticks: Array<object>, totalLines: number, truncated: boolean,
64
+ * stats: { added: number, removed: number, unchanged: number } }}
65
+ */
66
+ export function buildPromptsChangeAxis(baseText, currentText) {
67
+ const diff = buildLineDiff(baseText, currentText);
68
+ const totalLines = Math.max(1, diff.newLineCount);
69
+ const runs = [];
70
+ let run = null;
71
+ let newCursor = 0;
72
+ const flushRun = () => {
73
+ if (run) {
74
+ runs.push(run);
75
+ run = null;
76
+ }
77
+ };
78
+
79
+ for (const line of diff.lines) {
80
+ if (!line || !isChangedDiffType(line.type)) {
81
+ flushRun();
82
+ if (line && line.type === 'context' && typeof line.newNumber === 'number') {
83
+ newCursor = line.newNumber;
84
+ }
85
+ continue;
86
+ }
87
+ if (!run) {
88
+ run = {
89
+ anchor: line.type === 'add' && typeof line.newNumber === 'number'
90
+ ? line.newNumber - 1
91
+ : newCursor,
92
+ startLine: line.type === 'add' && typeof line.newNumber === 'number'
93
+ ? line.newNumber
94
+ : newCursor + 1,
95
+ added: 0,
96
+ removed: 0
97
+ };
98
+ }
99
+ if (line.type === 'add') {
100
+ run.added += 1;
101
+ } else {
102
+ run.removed += 1;
103
+ }
104
+ }
105
+ flushRun();
106
+
107
+ const { ticks, truncated } = capAxisTicks(runs, { total: totalLines });
108
+ return {
109
+ ticks,
110
+ totalLines,
111
+ truncated,
112
+ stats: diff.stats
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Build change-axis ticks for the diff preview view. Tick positions map to
118
+ * rendered row indexes so clicks scroll the diff list to the exact row.
119
+ *
120
+ * @param {Array<object>} diffLines rendered diff rows ({type, value, oldNumber, newNumber})
121
+ * @returns {{ ticks: Array<object>, rowCount: number, truncated: boolean }}
122
+ */
123
+ export function buildPromptsDiffAxis(diffLines) {
124
+ const rows = Array.isArray(diffLines) ? diffLines.filter((line) => line && line.type) : [];
125
+ const rowCount = rows.length;
126
+ const runs = [];
127
+ let run = null;
128
+ const flushRun = () => {
129
+ if (run) {
130
+ runs.push(run);
131
+ run = null;
132
+ }
133
+ };
134
+
135
+ rows.forEach((line, index) => {
136
+ if (!isChangedDiffType(line.type)) {
137
+ flushRun();
138
+ return;
139
+ }
140
+ if (!run) {
141
+ run = {
142
+ anchor: index,
143
+ rowIndex: index,
144
+ startLine: line.type === 'add' && typeof line.newNumber === 'number'
145
+ ? line.newNumber
146
+ : (typeof line.oldNumber === 'number' ? line.oldNumber : index + 1),
147
+ added: 0,
148
+ removed: 0
149
+ };
150
+ }
151
+ if (line.type === 'add') {
152
+ run.added += 1;
153
+ } else {
154
+ run.removed += 1;
155
+ }
156
+ });
157
+ flushRun();
158
+
159
+ const { ticks, truncated } = capAxisTicks(runs, { total: Math.max(1, rowCount) });
160
+ return {
161
+ ticks,
162
+ rowCount,
163
+ truncated
164
+ };
165
+ }
166
+
167
+ /**
168
+ * Proportional scroll position for jumping to a line inside the editor
169
+ * textarea. The textarea scrolls internally (min-height contract in
170
+ * prompts-editor.css), so scrollTop maps linearly onto the line space.
171
+ *
172
+ * @param {number} scrollHeight textarea scrollHeight
173
+ * @param {number} clientHeight textarea clientHeight
174
+ * @param {number} lineIndex 1-based target line in the current content
175
+ * @param {number} totalLines total line count of the current content
176
+ * @returns {number} clamped scrollTop value
177
+ */
178
+ export function computePromptsAxisScrollTop(scrollHeight, clientHeight, lineIndex, totalLines) {
179
+ const maxScroll = Math.max(0, (Number(scrollHeight) || 0) - (Number(clientHeight) || 0));
180
+ if (maxScroll <= 0) {
181
+ return 0;
182
+ }
183
+ const safeTotal = Math.max(1, Number(totalLines) || 1);
184
+ const safeLine = Math.max(1, Number(lineIndex) || 1);
185
+ const ratio = Math.min(1, Math.max(0, (safeLine - 1) / Math.max(1, safeTotal - 1)));
186
+ return Math.min(maxScroll, Math.round(maxScroll * ratio));
187
+ }
@@ -7,37 +7,68 @@
7
7
  * State contract (declared in app.js data()):
8
8
  * promptsPreviewEnabled, promptsPreviewCollapsed, promptsMobileView,
9
9
  * promptsPreviewLibsMissing, agentsHighlightHtml, sysPromptHighlightHtml,
10
- * agentsPreviewHtml, sysPromptPreviewHtml
10
+ * agentsPreviewHtml, sysPromptPreviewHtml, agentsChangeAxis, sysChangeAxis
11
11
  * Template contract:
12
12
  * refs promptsAgentsTextarea / promptsSysTextarea on the two textareas,
13
- * refs promptsAgentsHighlight / promptsSysHighlight on the backdrop <pre>.
13
+ * refs promptsAgentsHighlight / promptsSysHighlight on the backdrop <pre>,
14
+ * refs promptsAgentsDiffView / promptsSysDiffView on the two diff row containers.
14
15
  */
15
16
 
16
17
  import {
17
18
  buildMarkdownPreviewHtml,
18
19
  highlightMarkdownText
19
20
  } from '../logic.markdown-editor.mjs';
21
+ import {
22
+ buildPromptsChangeAxis,
23
+ buildPromptsDiffAxis,
24
+ computePromptsAxisScrollTop
25
+ } from '../logic.prompts-change-axis.mjs';
20
26
 
21
27
  const PROMPTS_HIGHLIGHT_DEBOUNCE_MS = 150;
22
28
  const PROMPTS_PREVIEW_DEBOUNCE_MS = 300;
29
+ const PROMPTS_AXIS_DEBOUNCE_MS = 300;
23
30
 
24
31
  const PROMPTS_EDITOR_META = {
25
32
  agents: {
26
33
  contentField: 'agentsContent',
34
+ originalField: 'agentsOriginalContent',
27
35
  highlightField: 'agentsHighlightHtml',
28
36
  previewField: 'agentsPreviewHtml',
37
+ axisField: 'agentsChangeAxis',
38
+ diffLinesField: 'agentsDiffLines',
39
+ diffStatsField: 'agentsDiffStats',
40
+ diffVisibleField: 'agentsDiffVisible',
41
+ loadingField: 'agentsLoading',
42
+ savingField: 'agentsSaving',
43
+ diffLoadingField: 'agentsDiffLoading',
44
+ prepareDiffMethod: 'prepareAgentsDiff',
29
45
  textareaRef: 'promptsAgentsTextarea',
30
- highlightRef: 'promptsAgentsHighlight'
46
+ highlightRef: 'promptsAgentsHighlight',
47
+ diffViewRef: 'promptsAgentsDiffView'
31
48
  },
32
49
  sys: {
33
50
  contentField: 'sysPromptContent',
51
+ originalField: 'sysPromptOriginalContent',
34
52
  highlightField: 'sysPromptHighlightHtml',
35
53
  previewField: 'sysPromptPreviewHtml',
54
+ axisField: 'sysChangeAxis',
55
+ diffLinesField: 'sysPromptDiffLines',
56
+ diffStatsField: 'sysPromptDiffStats',
57
+ diffVisibleField: 'sysPromptDiffVisible',
58
+ loadingField: 'sysPromptLoading',
59
+ savingField: 'sysPromptSaving',
60
+ diffLoadingField: 'sysPromptDiffLoading',
61
+ prepareDiffMethod: 'prepareSysPromptDiff',
36
62
  textareaRef: 'promptsSysTextarea',
37
- highlightRef: 'promptsSysHighlight'
63
+ highlightRef: 'promptsSysHighlight',
64
+ diffViewRef: 'promptsSysDiffView'
38
65
  }
39
66
  };
40
67
 
68
+ function emptyPromptsChangeAxis() {
69
+ return { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 };
70
+ }
71
+
41
72
  function resolvePromptsHljs() {
42
73
  if (typeof window !== 'undefined' && window.hljs) {
43
74
  return window.hljs;
@@ -138,6 +169,145 @@ export function createPromptsEditorMethods() {
138
169
  schedulePromptsEditorRefresh(key) {
139
170
  this.schedulePromptsHighlight(key);
140
171
  this.schedulePromptsPreview(key);
172
+ this.schedulePromptsChangeAxis(key);
173
+ },
174
+
175
+ refreshPromptsChangeAxis(key) {
176
+ const meta = this.promptsEditorMeta(key);
177
+ if (!meta) {
178
+ return;
179
+ }
180
+ if (this[meta.diffVisibleField]) {
181
+ const axis = buildPromptsDiffAxis(this[meta.diffLinesField]);
182
+ const stats = this[meta.diffStatsField];
183
+ this[meta.axisField] = {
184
+ mode: 'diff',
185
+ ticks: axis.truncated ? [] : axis.ticks,
186
+ truncated: axis.truncated,
187
+ totalLines: axis.rowCount,
188
+ added: Number(stats && stats.added) || 0,
189
+ removed: Number(stats && stats.removed) || 0
190
+ };
191
+ this.consumePromptsAxisPendingJump(key);
192
+ return;
193
+ }
194
+ this.clearPromptsAxisPendingJump(key);
195
+ const current = this[meta.contentField];
196
+ const original = this[meta.originalField];
197
+ if (typeof current !== 'string' || current === original) {
198
+ this[meta.axisField] = emptyPromptsChangeAxis();
199
+ return;
200
+ }
201
+ const axis = buildPromptsChangeAxis(typeof original === 'string' ? original : '', current);
202
+ this[meta.axisField] = {
203
+ mode: 'edit',
204
+ ticks: axis.truncated ? [] : axis.ticks,
205
+ truncated: axis.truncated,
206
+ totalLines: axis.totalLines,
207
+ added: Number(axis.stats && axis.stats.added) || 0,
208
+ removed: Number(axis.stats && axis.stats.removed) || 0
209
+ };
210
+ },
211
+
212
+ schedulePromptsChangeAxis(key) {
213
+ const meta = this.promptsEditorMeta(key);
214
+ if (!meta) {
215
+ return;
216
+ }
217
+ if (!this._promptsAxisTimers) {
218
+ this._promptsAxisTimers = {};
219
+ }
220
+ if (this._promptsAxisTimers[key]) {
221
+ clearTimeout(this._promptsAxisTimers[key]);
222
+ }
223
+ this._promptsAxisTimers[key] = setTimeout(() => {
224
+ this._promptsAxisTimers[key] = null;
225
+ this.refreshPromptsChangeAxis(key);
226
+ }, PROMPTS_AXIS_DEBOUNCE_MS);
227
+ },
228
+
229
+ jumpToPromptsChangeTick(key, tick) {
230
+ const meta = this.promptsEditorMeta(key);
231
+ const axis = meta ? this[meta.axisField] : null;
232
+ if (!meta || !axis || !tick) {
233
+ return;
234
+ }
235
+ if (axis.mode === 'diff') {
236
+ const view = this.$refs && this.$refs[meta.diffViewRef];
237
+ const rows = view && view.children ? Array.prototype.slice.call(view.children) : [];
238
+ const target = rows[tick.rowIndex];
239
+ if (target && typeof target.scrollIntoView === 'function') {
240
+ target.scrollIntoView({ block: 'center' });
241
+ }
242
+ return;
243
+ }
244
+ // Edit mode: a tick click opens the red/green comparison view
245
+ // focused on this hunk, so the user sees WHAT changed (old vs new),
246
+ // not just where. prepareAgentsDiff / prepareSysPromptDiff are the
247
+ // same preview entry the save/eye button uses; both settle their
248
+ // own error state internally and never reject.
249
+ const canOpenDiff = typeof this[meta.prepareDiffMethod] === 'function'
250
+ && !this[meta.loadingField]
251
+ && !this[meta.savingField]
252
+ && !this[meta.diffLoadingField];
253
+ if (canOpenDiff) {
254
+ this._promptsAxisPendingJump = { key, startLine: tick.startLine };
255
+ this[meta.prepareDiffMethod]();
256
+ return;
257
+ }
258
+ // Fallback when the diff flow is not wired on the context (isolated
259
+ // method usage): keep the legacy proportional editor scroll so the
260
+ // click still produces an observable move instead of a silent no-op.
261
+ const textarea = this.$refs && this.$refs[meta.textareaRef];
262
+ if (!textarea || typeof textarea.scrollTop !== 'number') {
263
+ return;
264
+ }
265
+ textarea.scrollTop = computePromptsAxisScrollTop(
266
+ textarea.scrollHeight,
267
+ textarea.clientHeight,
268
+ tick.startLine,
269
+ axis.totalLines
270
+ );
271
+ this.syncPromptsOverlayScroll(key);
272
+ },
273
+
274
+ clearPromptsAxisPendingJump(key) {
275
+ if (this._promptsAxisPendingJump && this._promptsAxisPendingJump.key === key) {
276
+ this._promptsAxisPendingJump = null;
277
+ }
278
+ },
279
+
280
+ consumePromptsAxisPendingJump(key) {
281
+ const pending = this._promptsAxisPendingJump;
282
+ if (!pending || pending.key !== key) {
283
+ return;
284
+ }
285
+ const meta = this.promptsEditorMeta(key);
286
+ if (!meta) {
287
+ this._promptsAxisPendingJump = null;
288
+ return;
289
+ }
290
+ const axis = this[meta.axisField];
291
+ if (!axis || axis.mode !== 'diff' || !axis.ticks.length) {
292
+ return;
293
+ }
294
+ this._promptsAxisPendingJump = null;
295
+ const target = axis.ticks.find((tick) => tick.startLine === pending.startLine)
296
+ || axis.ticks.find((tick) => tick.startLine >= pending.startLine)
297
+ || axis.ticks[axis.ticks.length - 1];
298
+ const applyScroll = () => {
299
+ const view = this.$refs && this.$refs[meta.diffViewRef];
300
+ const rows = view && view.children ? Array.prototype.slice.call(view.children) : [];
301
+ const row = rows[target.rowIndex];
302
+ if (row && typeof row.scrollIntoView === 'function') {
303
+ row.scrollIntoView({ block: 'center' });
304
+ }
305
+ };
306
+ if (typeof this.$nextTick === 'function') {
307
+ this.$nextTick(applyScroll);
308
+ } else {
309
+ applyScroll();
310
+ }
141
311
  },
142
312
 
143
313
  setPromptsMobileView(view) {
@@ -148,6 +318,9 @@ export function createPromptsEditorMethods() {
148
318
 
149
319
  togglePromptsPreviewCollapsed() {
150
320
  this.promptsPreviewCollapsed = !this.promptsPreviewCollapsed;
321
+ if (typeof this.persistWebUiPreferences === 'function') {
322
+ this.persistWebUiPreferences({ promptsPreviewCollapsed: this.promptsPreviewCollapsed });
323
+ }
151
324
  }
152
325
  };
153
326
  }
@@ -341,6 +341,7 @@ export function createWebUiPreferencesMethods(options = {}) {
341
341
  : this.configTemplateDiffConfirmEnabled !== false,
342
342
  sessionsUsageTimeRange: normalizeUsageTimeRange(hasOwn(source, 'sessionsUsageTimeRange') ? source.sessionsUsageTimeRange : this.sessionsUsageTimeRange),
343
343
  promptsSubTab: normalizePromptsSubTab(hasOwn(source, 'promptsSubTab') ? source.promptsSubTab : this.promptsSubTab),
344
+ promptsPreviewCollapsed: normalizeBoolean(hasOwn(source, 'promptsPreviewCollapsed') ? source.promptsPreviewCollapsed : this.promptsPreviewCollapsed, true),
344
345
  sysPromptScope: normalizeSysPromptScope(hasOwn(source, 'sysPromptScope') ? source.sysPromptScope : this.sysPromptScope),
345
346
  sysPromptMode: normalizeSysPromptMode(hasOwn(source, 'sysPromptMode') ? source.sysPromptMode : this.sysPromptMode),
346
347
  promptPresets: normalizePromptPresets(hasOwn(source, 'promptPresets') ? source.promptPresets : this.promptPresets),
@@ -398,6 +399,9 @@ export function createWebUiPreferencesMethods(options = {}) {
398
399
  if (typeof source.promptsSubTab === 'string') {
399
400
  this.promptsSubTab = normalizePromptsSubTab(source.promptsSubTab);
400
401
  }
402
+ if (hasOwn(source, 'promptsPreviewCollapsed')) {
403
+ this.promptsPreviewCollapsed = normalizeBoolean(source.promptsPreviewCollapsed, true);
404
+ }
401
405
  if (typeof source.sysPromptScope === 'string') {
402
406
  this.sysPromptScope = normalizeSysPromptScope(source.sysPromptScope);
403
407
  }
@@ -241,6 +241,9 @@ const en = Object.freeze({
241
241
  'prompts.editor.preview': 'Preview',
242
242
  'prompts.editor.previewUnavailable': 'Preview components missing (marked / DOMPurify), please refresh the page',
243
243
  'prompts.editor.previewEmpty': 'Nothing to preview yet',
244
+ 'prompts.editor.changeAxis': 'Change navigation axis',
245
+ 'prompts.editor.changeAxisTick': 'Line {line} (+{added} / -{removed})',
246
+ 'prompts.editor.changeAxisTruncated': 'Too many changed regions; collapsed to summary',
244
247
  'prompts.presets.title': 'Presets',
245
248
  'prompts.presets.hint': 'Manage presets.',
246
249
  'prompts.presets.namePlaceholder': 'Preset name',
@@ -242,6 +242,9 @@ const ja = Object.freeze({
242
242
  'prompts.editor.preview': 'プレビュー',
243
243
  'prompts.editor.previewUnavailable': 'プレビュー用コンポーネント(marked / DOMPurify)が読み込まれていません。ページを再読み込みしてください',
244
244
  'prompts.editor.previewEmpty': 'プレビューする内容がありません',
245
+ 'prompts.editor.changeAxis': '変更ナビゲーション軸',
246
+ 'prompts.editor.changeAxisTick': '{line} 行目(+{added} / -{removed})',
247
+ 'prompts.editor.changeAxisTruncated': '変更領域が多すぎるため、統計に折りたたまれました',
245
248
  'prompts.presets.title': 'プリセット',
246
249
  'prompts.presets.hint': 'プリセットを管理します。',
247
250
  'prompts.presets.namePlaceholder': 'プリセット名',
@@ -259,6 +259,9 @@ const vi = Object.freeze({
259
259
  'prompts.editor.preview': 'Xem trước',
260
260
  'prompts.editor.previewUnavailable': 'Thư viện xem trước (marked / DOMPurify) chưa được tải, hãy tải lại trang',
261
261
  'prompts.editor.previewEmpty': 'Chưa có nội dung để xem trước',
262
+ 'prompts.editor.changeAxis': 'Trục điều hướng thay đổi',
263
+ 'prompts.editor.changeAxisTick': 'Dòng {line} (+{added} / -{removed})',
264
+ 'prompts.editor.changeAxisTruncated': 'Quá nhiều vùng thay đổi; đã thu gọn thành thống kê',
262
265
  'prompts.presets.title': 'Preset',
263
266
  'prompts.presets.hint': 'Quản lý preset.',
264
267
  'prompts.presets.namePlaceholder': 'Tên preset',
@@ -241,6 +241,9 @@ const zhTw = Object.freeze({
241
241
  'prompts.editor.preview': '預覽',
242
242
  'prompts.editor.previewUnavailable': '預覽元件未載入(marked / DOMPurify 缺失),請重新整理頁面',
243
243
  'prompts.editor.previewEmpty': '尚無內容可預覽',
244
+ 'prompts.editor.changeAxis': '改動導覽軸',
245
+ 'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
246
+ 'prompts.editor.changeAxisTruncated': '改動區域過多,已摺疊為統計',
244
247
  'prompts.presets.title': '預設池',
245
248
  'prompts.presets.hint': '管理預設。',
246
249
  'prompts.presets.namePlaceholder': '預設名稱',
@@ -241,6 +241,9 @@ const zh = Object.freeze({
241
241
  'prompts.editor.preview': '预览',
242
242
  'prompts.editor.previewUnavailable': '预览组件未加载(marked / DOMPurify 缺失),请刷新页面',
243
243
  'prompts.editor.previewEmpty': '暂无内容可预览',
244
+ 'prompts.editor.changeAxis': '改动导航轴',
245
+ 'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
246
+ 'prompts.editor.changeAxisTruncated': '改动区域过多,已折叠为统计',
244
247
  'prompts.presets.title': '预设池',
245
248
  'prompts.presets.hint': '管理预设。',
246
249
  'prompts.presets.namePlaceholder': '预设名称',
@@ -189,15 +189,20 @@
189
189
  <div v-else-if="agentsDiffError" class="state-message error">{{ agentsDiffError }}</div>
190
190
  <div v-else-if="agentsDiffTruncated" class="agents-diff-empty">{{ t('diff.tooLargeSkip') }}</div>
191
191
  <div v-else-if="!agentsDiffHasChanges" class="agents-diff-empty">{{ t('diff.noChanges') }}</div>
192
- <div v-else class="agents-diff-view agents-diff-editor">
193
- <div
194
- v-for="(line, index) in agentsDiffLines"
195
- :key="line.key || (line.type + '-' + index)"
196
- :class="['agents-diff-line', line.type]">
197
- <span class="agents-diff-line-sign">
198
- {{ line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ') }}
199
- </span>
200
- <span class="agents-diff-line-text">{{ line.value }}</span>
192
+ <div v-else class="prompts-diff-frame">
193
+ <div v-if="agentsChangeAxis.ticks.length" class="prompts-change-axis prompts-change-axis--diff" role="navigation" :aria-label="t('prompts.editor.changeAxis')">
194
+ <button v-for="(tick, index) in agentsChangeAxis.ticks" :key="'agents-diff-axis-' + index" type="button" class="prompts-change-tick" :class="'prompts-change-tick--' + tick.kind" :style="{ top: tick.top + '%', height: tick.height + '%' }" :title="t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed })" @click="jumpToPromptsChangeTick('agents', tick)"></button>
195
+ </div>
196
+ <div class="agents-diff-view agents-diff-editor" ref="promptsAgentsDiffView">
197
+ <div
198
+ v-for="(line, index) in agentsDiffLines"
199
+ :key="line.key || (line.type + '-' + index)"
200
+ :class="['agents-diff-line', line.type]">
201
+ <span class="agents-diff-line-sign">
202
+ {{ line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ') }}
203
+ </span>
204
+ <span class="agents-diff-line-text">{{ line.value }}</span>
205
+ </div>
201
206
  </div>
202
207
  </div>
203
208
  </div>
@@ -228,6 +233,13 @@
228
233
  @input="onAgentsContentInput"
229
234
  @scroll="syncPromptsOverlayScroll('agents')"
230
235
  :placeholder="t(promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')"></textarea>
236
+ <div v-if="!agentsDiffVisible && agentsChangeAxis.ticks.length" class="prompts-change-axis" role="navigation" :aria-label="t('prompts.editor.changeAxis')">
237
+ <button v-for="(tick, index) in agentsChangeAxis.ticks" :key="'agents-change-axis-' + index" type="button" class="prompts-change-tick" :class="'prompts-change-tick--' + tick.kind" :style="{ top: tick.top + '%', height: tick.height + '%' }" :title="t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed })" @click="jumpToPromptsChangeTick('agents', tick)"></button>
238
+ </div>
239
+ <div v-else-if="!agentsDiffVisible && agentsChangeAxis.truncated" class="prompts-change-axis prompts-change-axis--summary" :title="t('prompts.editor.changeAxisTruncated')">
240
+ <span class="prompts-change-summary add">+{{ agentsChangeAxis.added }}</span>
241
+ <span class="prompts-change-summary del">-{{ agentsChangeAxis.removed }}</span>
242
+ </div>
231
243
  </div>
232
244
  <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
233
245
  <div v-if="promptsPreviewLibsMissing" class="state-message error">{{ t('prompts.editor.previewUnavailable') }}</div>
@@ -331,7 +343,11 @@
331
343
  <div v-else-if="sysPromptDiffError" class="state-message error">{{ sysPromptDiffError }}</div>
332
344
  <div v-else-if="sysPromptDiffTruncated" class="agents-diff-empty">{{ t('diff.tooLargeSkip') }}</div>
333
345
  <div v-else-if="!sysPromptDiffHasChangesValue" class="agents-diff-empty">{{ t('diff.noChanges') }}</div>
334
- <div v-else class="agents-diff-view agents-diff-editor">
346
+ <div v-else class="prompts-diff-frame">
347
+ <div v-if="sysChangeAxis.ticks.length" class="prompts-change-axis prompts-change-axis--diff" role="navigation" :aria-label="t('prompts.editor.changeAxis')">
348
+ <button v-for="(tick, index) in sysChangeAxis.ticks" :key="'sys-diff-axis-' + index" type="button" class="prompts-change-tick" :class="'prompts-change-tick--' + tick.kind" :style="{ top: tick.top + '%', height: tick.height + '%' }" :title="t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed })" @click="jumpToPromptsChangeTick('sys', tick)"></button>
349
+ </div>
350
+ <div class="agents-diff-view agents-diff-editor" ref="promptsSysDiffView">
335
351
  <div
336
352
  v-for="(line, index) in sysPromptDiffLines"
337
353
  :key="'sys-prompt-diff-' + index"
@@ -343,6 +359,7 @@
343
359
  </div>
344
360
  </div>
345
361
  </div>
362
+ </div>
346
363
  <div class="prompts-md-toolbar" role="toolbar" :aria-label="t('prompts.editor.toolbar')">
347
364
  <div class="prompts-md-toolbar-group prompts-md-toolbar-group--view">
348
365
  <div v-if="promptsPreviewEnabled" class="prompts-mobile-view-switch" role="tablist" :aria-label="t('prompts.editor.previewToggle')">
@@ -369,6 +386,13 @@
369
386
  :readonly="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible"
370
387
  @scroll="syncPromptsOverlayScroll('sys')"
371
388
  :placeholder="t('sysPrompt.placeholder')"></textarea>
389
+ <div v-if="!sysPromptDiffVisible && sysChangeAxis.ticks.length" class="prompts-change-axis" role="navigation" :aria-label="t('prompts.editor.changeAxis')">
390
+ <button v-for="(tick, index) in sysChangeAxis.ticks" :key="'sys-change-axis-' + index" type="button" class="prompts-change-tick" :class="'prompts-change-tick--' + tick.kind" :style="{ top: tick.top + '%', height: tick.height + '%' }" :title="t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed })" @click="jumpToPromptsChangeTick('sys', tick)"></button>
391
+ </div>
392
+ <div v-else-if="!sysPromptDiffVisible && sysChangeAxis.truncated" class="prompts-change-axis prompts-change-axis--summary" :title="t('prompts.editor.changeAxisTruncated')">
393
+ <span class="prompts-change-summary add">+{{ sysChangeAxis.added }}</span>
394
+ <span class="prompts-change-summary del">-{{ sysChangeAxis.removed }}</span>
395
+ </div>
372
396
  </div>
373
397
  <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
374
398
  <div v-if="promptsPreviewLibsMissing" class="state-message error">{{ t('prompts.editor.previewUnavailable') }}</div>
@@ -7300,17 +7300,41 @@ return function render(_ctx, _cache) {
7300
7300
  }, _toDisplayString(_ctx.t('diff.noChanges')), 1 /* TEXT */))
7301
7301
  : (_openBlock(), _createElementBlock("div", {
7302
7302
  key: 5,
7303
- class: "agents-diff-view agents-diff-editor"
7303
+ class: "prompts-diff-frame"
7304
7304
  }, [
7305
- (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.agentsDiffLines, (line, index) => {
7306
- return (_openBlock(), _createElementBlock("div", {
7307
- key: line.key || (line.type + '-' + index),
7308
- class: _normalizeClass(['agents-diff-line', line.type])
7309
- }, [
7310
- _createElementVNode("span", { class: "agents-diff-line-sign" }, _toDisplayString(line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ')), 1 /* TEXT */),
7311
- _createElementVNode("span", { class: "agents-diff-line-text" }, _toDisplayString(line.value), 1 /* TEXT */)
7312
- ], 2 /* CLASS */))
7313
- }), 128 /* KEYED_FRAGMENT */))
7305
+ (_ctx.agentsChangeAxis.ticks.length)
7306
+ ? (_openBlock(), _createElementBlock("div", {
7307
+ key: 0,
7308
+ class: "prompts-change-axis prompts-change-axis--diff",
7309
+ role: "navigation",
7310
+ "aria-label": _ctx.t('prompts.editor.changeAxis')
7311
+ }, [
7312
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.agentsChangeAxis.ticks, (tick, index) => {
7313
+ return (_openBlock(), _createElementBlock("button", {
7314
+ key: 'agents-diff-axis-' + index,
7315
+ type: "button",
7316
+ class: _normalizeClass(["prompts-change-tick", 'prompts-change-tick--' + tick.kind]),
7317
+ style: _normalizeStyle({ top: tick.top + '%', height: tick.height + '%' }),
7318
+ title: _ctx.t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed }),
7319
+ onClick: $event => (_ctx.jumpToPromptsChangeTick('agents', tick))
7320
+ }, null, 14 /* CLASS, STYLE, PROPS */, ["title", "onClick"]))
7321
+ }), 128 /* KEYED_FRAGMENT */))
7322
+ ], 8 /* PROPS */, ["aria-label"]))
7323
+ : _createCommentVNode("v-if", true),
7324
+ _createElementVNode("div", {
7325
+ class: "agents-diff-view agents-diff-editor",
7326
+ ref: "promptsAgentsDiffView"
7327
+ }, [
7328
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.agentsDiffLines, (line, index) => {
7329
+ return (_openBlock(), _createElementBlock("div", {
7330
+ key: line.key || (line.type + '-' + index),
7331
+ class: _normalizeClass(['agents-diff-line', line.type])
7332
+ }, [
7333
+ _createElementVNode("span", { class: "agents-diff-line-sign" }, _toDisplayString(line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ')), 1 /* TEXT */),
7334
+ _createElementVNode("span", { class: "agents-diff-line-text" }, _toDisplayString(line.value), 1 /* TEXT */)
7335
+ ], 2 /* CLASS */))
7336
+ }), 128 /* KEYED_FRAGMENT */))
7337
+ ], 512 /* NEED_PATCH */)
7314
7338
  ]))
7315
7339
  ]))
7316
7340
  : _createCommentVNode("v-if", true),
@@ -7400,7 +7424,35 @@ return function render(_ctx, _cache) {
7400
7424
  placeholder: _ctx.t(_ctx.promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')
7401
7425
  }, null, 40 /* PROPS, NEED_HYDRATION */, ["onUpdate:modelValue", "readonly", "onInput", "onScroll", "placeholder"]), [
7402
7426
  [_vModelText, _ctx.agentsContent]
7403
- ])
7427
+ ]),
7428
+ (!_ctx.agentsDiffVisible && _ctx.agentsChangeAxis.ticks.length)
7429
+ ? (_openBlock(), _createElementBlock("div", {
7430
+ key: 0,
7431
+ class: "prompts-change-axis",
7432
+ role: "navigation",
7433
+ "aria-label": _ctx.t('prompts.editor.changeAxis')
7434
+ }, [
7435
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.agentsChangeAxis.ticks, (tick, index) => {
7436
+ return (_openBlock(), _createElementBlock("button", {
7437
+ key: 'agents-change-axis-' + index,
7438
+ type: "button",
7439
+ class: _normalizeClass(["prompts-change-tick", 'prompts-change-tick--' + tick.kind]),
7440
+ style: _normalizeStyle({ top: tick.top + '%', height: tick.height + '%' }),
7441
+ title: _ctx.t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed }),
7442
+ onClick: $event => (_ctx.jumpToPromptsChangeTick('agents', tick))
7443
+ }, null, 14 /* CLASS, STYLE, PROPS */, ["title", "onClick"]))
7444
+ }), 128 /* KEYED_FRAGMENT */))
7445
+ ], 8 /* PROPS */, ["aria-label"]))
7446
+ : (!_ctx.agentsDiffVisible && _ctx.agentsChangeAxis.truncated)
7447
+ ? (_openBlock(), _createElementBlock("div", {
7448
+ key: 1,
7449
+ class: "prompts-change-axis prompts-change-axis--summary",
7450
+ title: _ctx.t('prompts.editor.changeAxisTruncated')
7451
+ }, [
7452
+ _createElementVNode("span", { class: "prompts-change-summary add" }, "+" + _toDisplayString(_ctx.agentsChangeAxis.added), 1 /* TEXT */),
7453
+ _createElementVNode("span", { class: "prompts-change-summary del" }, "-" + _toDisplayString(_ctx.agentsChangeAxis.removed), 1 /* TEXT */)
7454
+ ], 8 /* PROPS */, ["title"]))
7455
+ : _createCommentVNode("v-if", true)
7404
7456
  ]),
7405
7457
  (_ctx.promptsPreviewEnabled)
7406
7458
  ? (_openBlock(), _createElementBlock("div", {
@@ -7725,17 +7777,41 @@ return function render(_ctx, _cache) {
7725
7777
  }, _toDisplayString(_ctx.t('diff.noChanges')), 1 /* TEXT */))
7726
7778
  : (_openBlock(), _createElementBlock("div", {
7727
7779
  key: 5,
7728
- class: "agents-diff-view agents-diff-editor"
7780
+ class: "prompts-diff-frame"
7729
7781
  }, [
7730
- (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sysPromptDiffLines, (line, index) => {
7731
- return (_openBlock(), _createElementBlock("div", {
7732
- key: 'sys-prompt-diff-' + index,
7733
- class: _normalizeClass(['agents-diff-line', line.type])
7734
- }, [
7735
- _createElementVNode("span", { class: "agents-diff-line-sign" }, _toDisplayString(line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ')), 1 /* TEXT */),
7736
- _createElementVNode("span", { class: "agents-diff-line-text" }, _toDisplayString(line.value), 1 /* TEXT */)
7737
- ], 2 /* CLASS */))
7738
- }), 128 /* KEYED_FRAGMENT */))
7782
+ (_ctx.sysChangeAxis.ticks.length)
7783
+ ? (_openBlock(), _createElementBlock("div", {
7784
+ key: 0,
7785
+ class: "prompts-change-axis prompts-change-axis--diff",
7786
+ role: "navigation",
7787
+ "aria-label": _ctx.t('prompts.editor.changeAxis')
7788
+ }, [
7789
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sysChangeAxis.ticks, (tick, index) => {
7790
+ return (_openBlock(), _createElementBlock("button", {
7791
+ key: 'sys-diff-axis-' + index,
7792
+ type: "button",
7793
+ class: _normalizeClass(["prompts-change-tick", 'prompts-change-tick--' + tick.kind]),
7794
+ style: _normalizeStyle({ top: tick.top + '%', height: tick.height + '%' }),
7795
+ title: _ctx.t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed }),
7796
+ onClick: $event => (_ctx.jumpToPromptsChangeTick('sys', tick))
7797
+ }, null, 14 /* CLASS, STYLE, PROPS */, ["title", "onClick"]))
7798
+ }), 128 /* KEYED_FRAGMENT */))
7799
+ ], 8 /* PROPS */, ["aria-label"]))
7800
+ : _createCommentVNode("v-if", true),
7801
+ _createElementVNode("div", {
7802
+ class: "agents-diff-view agents-diff-editor",
7803
+ ref: "promptsSysDiffView"
7804
+ }, [
7805
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sysPromptDiffLines, (line, index) => {
7806
+ return (_openBlock(), _createElementBlock("div", {
7807
+ key: 'sys-prompt-diff-' + index,
7808
+ class: _normalizeClass(['agents-diff-line', line.type])
7809
+ }, [
7810
+ _createElementVNode("span", { class: "agents-diff-line-sign" }, _toDisplayString(line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ')), 1 /* TEXT */),
7811
+ _createElementVNode("span", { class: "agents-diff-line-text" }, _toDisplayString(line.value), 1 /* TEXT */)
7812
+ ], 2 /* CLASS */))
7813
+ }), 128 /* KEYED_FRAGMENT */))
7814
+ ], 512 /* NEED_PATCH */)
7739
7815
  ]))
7740
7816
  ]))
7741
7817
  : _createCommentVNode("v-if", true),
@@ -7824,7 +7900,35 @@ return function render(_ctx, _cache) {
7824
7900
  placeholder: _ctx.t('sysPrompt.placeholder')
7825
7901
  }, null, 40 /* PROPS, NEED_HYDRATION */, ["onUpdate:modelValue", "readonly", "onScroll", "placeholder"]), [
7826
7902
  [_vModelText, _ctx.sysPromptContent]
7827
- ])
7903
+ ]),
7904
+ (!_ctx.sysPromptDiffVisible && _ctx.sysChangeAxis.ticks.length)
7905
+ ? (_openBlock(), _createElementBlock("div", {
7906
+ key: 0,
7907
+ class: "prompts-change-axis",
7908
+ role: "navigation",
7909
+ "aria-label": _ctx.t('prompts.editor.changeAxis')
7910
+ }, [
7911
+ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(_ctx.sysChangeAxis.ticks, (tick, index) => {
7912
+ return (_openBlock(), _createElementBlock("button", {
7913
+ key: 'sys-change-axis-' + index,
7914
+ type: "button",
7915
+ class: _normalizeClass(["prompts-change-tick", 'prompts-change-tick--' + tick.kind]),
7916
+ style: _normalizeStyle({ top: tick.top + '%', height: tick.height + '%' }),
7917
+ title: _ctx.t('prompts.editor.changeAxisTick', { line: tick.startLine, added: tick.added, removed: tick.removed }),
7918
+ onClick: $event => (_ctx.jumpToPromptsChangeTick('sys', tick))
7919
+ }, null, 14 /* CLASS, STYLE, PROPS */, ["title", "onClick"]))
7920
+ }), 128 /* KEYED_FRAGMENT */))
7921
+ ], 8 /* PROPS */, ["aria-label"]))
7922
+ : (!_ctx.sysPromptDiffVisible && _ctx.sysChangeAxis.truncated)
7923
+ ? (_openBlock(), _createElementBlock("div", {
7924
+ key: 1,
7925
+ class: "prompts-change-axis prompts-change-axis--summary",
7926
+ title: _ctx.t('prompts.editor.changeAxisTruncated')
7927
+ }, [
7928
+ _createElementVNode("span", { class: "prompts-change-summary add" }, "+" + _toDisplayString(_ctx.sysChangeAxis.added), 1 /* TEXT */),
7929
+ _createElementVNode("span", { class: "prompts-change-summary del" }, "-" + _toDisplayString(_ctx.sysChangeAxis.removed), 1 /* TEXT */)
7930
+ ], 8 /* PROPS */, ["title"]))
7931
+ : _createCommentVNode("v-if", true)
7828
7932
  ]),
7829
7933
  (_ctx.promptsPreviewEnabled)
7830
7934
  ? (_openBlock(), _createElementBlock("div", {
@@ -291,6 +291,104 @@
291
291
  max-width: 100%;
292
292
  }
293
293
 
294
+
295
+ /*
296
+ * Change axis: clickable per-hunk navigation strip rendered on the
297
+ * right edge of the edit pane (edit mode) and of the diff frame
298
+ * (diff preview mode). Ticks are positioned by percentage of the
299
+ * content line space (edit) or of the rendered rows (diff).
300
+ */
301
+ .prompts-change-axis {
302
+ position: absolute;
303
+ top: 6px;
304
+ bottom: 6px;
305
+ right: 2px;
306
+ width: 10px;
307
+ z-index: 3;
308
+ pointer-events: none;
309
+ }
310
+
311
+ .prompts-change-tick {
312
+ position: absolute;
313
+ left: 0;
314
+ right: 0;
315
+ min-height: 8px;
316
+ padding: 0;
317
+ border: none;
318
+ border-radius: 3px;
319
+ cursor: pointer;
320
+ pointer-events: auto;
321
+ }
322
+
323
+ .prompts-change-tick:focus-visible {
324
+ outline: 2px solid var(--color-brand);
325
+ outline-offset: 1px;
326
+ }
327
+
328
+ .prompts-change-tick--add {
329
+ background: rgba(57, 181, 97, 0.5);
330
+ }
331
+
332
+ .prompts-change-tick--add:hover,
333
+ .prompts-change-tick--add:focus-visible {
334
+ background: rgba(57, 181, 97, 0.85);
335
+ }
336
+
337
+ .prompts-change-tick--del {
338
+ background: rgba(220, 95, 108, 0.5);
339
+ }
340
+
341
+ .prompts-change-tick--del:hover,
342
+ .prompts-change-tick--del:focus-visible {
343
+ background: rgba(220, 95, 108, 0.85);
344
+ }
345
+
346
+ .prompts-change-tick--mixed {
347
+ background: linear-gradient(180deg,
348
+ rgba(220, 95, 108, 0.5) 0%,
349
+ rgba(220, 95, 108, 0.5) 50%,
350
+ rgba(57, 181, 97, 0.5) 50%,
351
+ rgba(57, 181, 97, 0.5) 100%);
352
+ }
353
+
354
+ .prompts-change-tick--mixed:hover,
355
+ .prompts-change-tick--mixed:focus-visible {
356
+ background: linear-gradient(180deg,
357
+ rgba(220, 95, 108, 0.85) 0%,
358
+ rgba(220, 95, 108, 0.85) 50%,
359
+ rgba(57, 181, 97, 0.85) 50%,
360
+ rgba(57, 181, 97, 0.85) 100%);
361
+ }
362
+
363
+ .prompts-diff-frame {
364
+ position: relative;
365
+ }
366
+
367
+ .prompts-change-axis--summary {
368
+ display: flex;
369
+ flex-direction: column;
370
+ align-items: flex-end;
371
+ gap: 2px;
372
+ width: auto;
373
+ font-size: 10px;
374
+ font-weight: var(--font-weight-secondary);
375
+ }
376
+
377
+ .prompts-change-summary {
378
+ padding: 0 6px;
379
+ border-radius: 999px;
380
+ white-space: nowrap;
381
+ }
382
+
383
+ .prompts-change-summary.add {
384
+ color: #2b6a3b;
385
+ background: rgba(57, 181, 97, 0.12);
386
+ }
387
+
388
+ .prompts-change-summary.del {
389
+ color: #8a2f36;
390
+ background: rgba(220, 95, 108, 0.12);
391
+ }
294
392
  /* Mobile: force single column and edit/preview tab switching */
295
393
  @media (max-width: 767.98px) {
296
394
  .prompts-mobile-view-switch {
@@ -317,4 +415,8 @@
317
415
  .prompts-split-collapse-btn {
318
416
  display: none;
319
417
  }
418
+
419
+ .prompts-change-tick {
420
+ min-height: 12px;
421
+ }
320
422
  }