codexmate 0.1.21 → 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.
@@ -0,0 +1,326 @@
1
+ /**
2
+ * Prompts-panel Markdown editor methods: overlay highlight sync,
3
+ * mobile view switching, and the live split preview (marked + DOMPurify,
4
+ * debounced). One implementation is shared by the agentsContent editor
5
+ * and the sysPromptContent editor.
6
+ *
7
+ * State contract (declared in app.js data()):
8
+ * promptsPreviewEnabled, promptsPreviewCollapsed, promptsMobileView,
9
+ * promptsPreviewLibsMissing, agentsHighlightHtml, sysPromptHighlightHtml,
10
+ * agentsPreviewHtml, sysPromptPreviewHtml, agentsChangeAxis, sysChangeAxis
11
+ * Template contract:
12
+ * refs promptsAgentsTextarea / promptsSysTextarea on the two textareas,
13
+ * refs promptsAgentsHighlight / promptsSysHighlight on the backdrop <pre>,
14
+ * refs promptsAgentsDiffView / promptsSysDiffView on the two diff row containers.
15
+ */
16
+
17
+ import {
18
+ buildMarkdownPreviewHtml,
19
+ highlightMarkdownText
20
+ } from '../logic.markdown-editor.mjs';
21
+ import {
22
+ buildPromptsChangeAxis,
23
+ buildPromptsDiffAxis,
24
+ computePromptsAxisScrollTop
25
+ } from '../logic.prompts-change-axis.mjs';
26
+
27
+ const PROMPTS_HIGHLIGHT_DEBOUNCE_MS = 150;
28
+ const PROMPTS_PREVIEW_DEBOUNCE_MS = 300;
29
+ const PROMPTS_AXIS_DEBOUNCE_MS = 300;
30
+
31
+ const PROMPTS_EDITOR_META = {
32
+ agents: {
33
+ contentField: 'agentsContent',
34
+ originalField: 'agentsOriginalContent',
35
+ highlightField: 'agentsHighlightHtml',
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',
45
+ textareaRef: 'promptsAgentsTextarea',
46
+ highlightRef: 'promptsAgentsHighlight',
47
+ diffViewRef: 'promptsAgentsDiffView'
48
+ },
49
+ sys: {
50
+ contentField: 'sysPromptContent',
51
+ originalField: 'sysPromptOriginalContent',
52
+ highlightField: 'sysPromptHighlightHtml',
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',
62
+ textareaRef: 'promptsSysTextarea',
63
+ highlightRef: 'promptsSysHighlight',
64
+ diffViewRef: 'promptsSysDiffView'
65
+ }
66
+ };
67
+
68
+ function emptyPromptsChangeAxis() {
69
+ return { mode: 'edit', ticks: [], truncated: false, totalLines: 0, added: 0, removed: 0 };
70
+ }
71
+
72
+ function resolvePromptsHljs() {
73
+ if (typeof window !== 'undefined' && window.hljs) {
74
+ return window.hljs;
75
+ }
76
+ return null;
77
+ }
78
+
79
+ function resolvePromptsPreviewLibs() {
80
+ if (typeof window === 'undefined') {
81
+ return { marked: null, purify: null };
82
+ }
83
+ return { marked: window.marked || null, purify: window.DOMPurify || null };
84
+ }
85
+
86
+ export function createPromptsEditorMethods() {
87
+ return {
88
+ promptsEditorMeta(key) {
89
+ return PROMPTS_EDITOR_META[key] || null;
90
+ },
91
+
92
+
93
+ refreshPromptsHighlight(key) {
94
+ const meta = this.promptsEditorMeta(key);
95
+ if (!meta) {
96
+ return;
97
+ }
98
+ this[meta.highlightField] = highlightMarkdownText(this[meta.contentField], resolvePromptsHljs());
99
+ this.syncPromptsOverlayScroll(key);
100
+ },
101
+
102
+ schedulePromptsHighlight(key) {
103
+ const meta = this.promptsEditorMeta(key);
104
+ if (!meta) {
105
+ return;
106
+ }
107
+ if (!this._promptsHighlightTimers) {
108
+ this._promptsHighlightTimers = {};
109
+ }
110
+ if (this._promptsHighlightTimers[key]) {
111
+ clearTimeout(this._promptsHighlightTimers[key]);
112
+ }
113
+ this._promptsHighlightTimers[key] = setTimeout(() => {
114
+ this._promptsHighlightTimers[key] = null;
115
+ this.refreshPromptsHighlight(key);
116
+ }, PROMPTS_HIGHLIGHT_DEBOUNCE_MS);
117
+ },
118
+
119
+ syncPromptsOverlayScroll(key) {
120
+ const meta = this.promptsEditorMeta(key);
121
+ if (!meta || typeof this.$refs !== 'object' || !this.$refs) {
122
+ return;
123
+ }
124
+ const textarea = this.$refs[meta.textareaRef];
125
+ const backdrop = this.$refs[meta.highlightRef];
126
+ if (!textarea || !backdrop) {
127
+ return;
128
+ }
129
+ backdrop.scrollTop = textarea.scrollTop;
130
+ backdrop.scrollLeft = textarea.scrollLeft;
131
+ },
132
+
133
+ refreshPromptsPreview(key) {
134
+ const meta = this.promptsEditorMeta(key);
135
+ if (!meta) {
136
+ return;
137
+ }
138
+ if (!this.promptsPreviewEnabled) {
139
+ return;
140
+ }
141
+ const libs = resolvePromptsPreviewLibs();
142
+ const state = buildMarkdownPreviewHtml(this[meta.contentField], libs.marked, libs.purify);
143
+ if (state.unavailable) {
144
+ this.promptsPreviewLibsMissing = true;
145
+ this[meta.previewField] = '';
146
+ return;
147
+ }
148
+ this.promptsPreviewLibsMissing = false;
149
+ this[meta.previewField] = state.html;
150
+ },
151
+
152
+ schedulePromptsPreview(key) {
153
+ const meta = this.promptsEditorMeta(key);
154
+ if (!meta) {
155
+ return;
156
+ }
157
+ if (!this._promptsPreviewTimers) {
158
+ this._promptsPreviewTimers = {};
159
+ }
160
+ if (this._promptsPreviewTimers[key]) {
161
+ clearTimeout(this._promptsPreviewTimers[key]);
162
+ }
163
+ this._promptsPreviewTimers[key] = setTimeout(() => {
164
+ this._promptsPreviewTimers[key] = null;
165
+ this.refreshPromptsPreview(key);
166
+ }, PROMPTS_PREVIEW_DEBOUNCE_MS);
167
+ },
168
+
169
+ schedulePromptsEditorRefresh(key) {
170
+ this.schedulePromptsHighlight(key);
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
+ }
311
+ },
312
+
313
+ setPromptsMobileView(view) {
314
+ if (view === 'edit' || view === 'preview') {
315
+ this.promptsMobileView = view;
316
+ }
317
+ },
318
+
319
+ togglePromptsPreviewCollapsed() {
320
+ this.promptsPreviewCollapsed = !this.promptsPreviewCollapsed;
321
+ if (typeof this.persistWebUiPreferences === 'function') {
322
+ this.persistWebUiPreferences({ promptsPreviewCollapsed: this.promptsPreviewCollapsed });
323
+ }
324
+ }
325
+ };
326
+ }
@@ -174,28 +174,61 @@ export function createSessionActionMethods(options = {}) {
174
174
 
175
175
  async copySessionWorkspaceBrief() {
176
176
  const summary = this.activeSessionWorkspaceSummary;
177
- const text = summary && typeof summary.briefText === 'string'
177
+ const brief = summary && typeof summary.briefText === 'string'
178
178
  ? summary.briefText.trim()
179
179
  : '';
180
- if (!text) {
180
+ if (!brief) {
181
181
  this.showMessage(this.t('sessions.workspace.copy.empty'), 'error');
182
182
  return;
183
183
  }
184
+ const session = this.activeSession;
185
+ let exported;
186
+ let exportResult;
187
+ try {
188
+ const res = await api('export-session', {
189
+ source: session.source,
190
+ sessionId: session.sessionId,
191
+ filePath: session.filePath
192
+ });
193
+ if (!res || typeof res !== 'object' || (typeof res.error === 'string' && res.error.trim())) {
194
+ this.showMessage((res && res.error) || this.t('toast.copy.fail'), 'error');
195
+ return;
196
+ }
197
+ exported = typeof res.content === 'string' ? res.content.trim() : '';
198
+ if (!exported) {
199
+ this.showMessage(this.t('toast.copy.fail'), 'error');
200
+ return;
201
+ }
202
+ exportResult = res;
203
+ } catch (e) {
204
+ this.showMessage(this.t('toast.copy.fail'), 'error');
205
+ return;
206
+ }
207
+ const text = `${brief}\n\n---\n\n${exported}`;
184
208
  const ok = this.fallbackCopyText(text);
185
209
  if (ok) {
186
- this.showMessage(this.t('sessions.workspace.copy.success'), 'success');
210
+ this.notifyBriefCopied(exportResult);
187
211
  return;
188
212
  }
189
213
  try {
190
214
  if (navigator.clipboard && window.isSecureContext) {
191
215
  await navigator.clipboard.writeText(text);
192
- this.showMessage(this.t('sessions.workspace.copy.success'), 'success');
216
+ this.notifyBriefCopied(exportResult);
193
217
  return;
194
218
  }
195
219
  } catch (_) {}
196
220
  this.showMessage(this.t('toast.copy.fail'), 'error');
197
221
  },
198
222
 
223
+ notifyBriefCopied(exportResult) {
224
+ if (exportResult && exportResult.truncated) {
225
+ const maxLabel = exportResult.maxMessages === 'all' ? 'all' : exportResult.maxMessages;
226
+ this.showMessage(`已复制工作简报(会话全文已截断:最多 ${maxLabel} 条消息)`, 'info');
227
+ return;
228
+ }
229
+ this.showMessage(this.t('sessions.workspace.copy.success'), 'success');
230
+ },
231
+
199
232
  getSessionExportKey(session) {
200
233
  return `${session.source || 'unknown'}:${session.sessionId || ''}:${session.filePath || ''}`;
201
234
  },
@@ -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
  }
@@ -233,6 +233,17 @@ const en = Object.freeze({
233
233
  'prompts.subTab.project': 'CLAUDE.md',
234
234
  'prompts.subTab.system': 'System Prompt (Pi)',
235
235
  'prompts.subTab.presets': 'Preset pool',
236
+ 'prompts.editor.toolbar': 'Markdown toolbar',
237
+ 'prompts.editor.collapsePreview': 'Collapse preview',
238
+ 'prompts.editor.expandPreview': 'Expand preview',
239
+ 'prompts.editor.previewToggle': 'Live preview',
240
+ 'prompts.editor.edit': 'Edit',
241
+ 'prompts.editor.preview': 'Preview',
242
+ 'prompts.editor.previewUnavailable': 'Preview components missing (marked / DOMPurify), please refresh the page',
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',
236
247
  'prompts.presets.title': 'Presets',
237
248
  'prompts.presets.hint': 'Manage presets.',
238
249
  'prompts.presets.namePlaceholder': 'Preset name',
@@ -234,6 +234,17 @@ const ja = Object.freeze({
234
234
  'prompts.subTab.project': 'CLAUDE.md',
235
235
  'prompts.subTab.system': 'System Prompt (Pi)',
236
236
  'prompts.subTab.presets': 'プリセット',
237
+ 'prompts.editor.toolbar': 'Markdown ツールバー',
238
+ 'prompts.editor.collapsePreview': 'プレビューを折りたたむ',
239
+ 'prompts.editor.expandPreview': 'プレビューを展開',
240
+ 'prompts.editor.previewToggle': 'ライブプレビュー',
241
+ 'prompts.editor.edit': '編集',
242
+ 'prompts.editor.preview': 'プレビュー',
243
+ 'prompts.editor.previewUnavailable': 'プレビュー用コンポーネント(marked / DOMPurify)が読み込まれていません。ページを再読み込みしてください',
244
+ 'prompts.editor.previewEmpty': 'プレビューする内容がありません',
245
+ 'prompts.editor.changeAxis': '変更ナビゲーション軸',
246
+ 'prompts.editor.changeAxisTick': '{line} 行目(+{added} / -{removed})',
247
+ 'prompts.editor.changeAxisTruncated': '変更領域が多すぎるため、統計に折りたたまれました',
237
248
  'prompts.presets.title': 'プリセット',
238
249
  'prompts.presets.hint': 'プリセットを管理します。',
239
250
  'prompts.presets.namePlaceholder': 'プリセット名',
@@ -251,6 +251,17 @@ const vi = Object.freeze({
251
251
  'prompts.subTab.project': 'CLAUDE.md',
252
252
  'prompts.subTab.system': 'System Prompt (Pi)',
253
253
  'prompts.subTab.presets': 'Kho preset',
254
+ 'prompts.editor.toolbar': 'Thanh công cụ Markdown',
255
+ 'prompts.editor.collapsePreview': 'Thu gọn bản xem trước',
256
+ 'prompts.editor.expandPreview': 'Mở rộng bản xem trước',
257
+ 'prompts.editor.previewToggle': 'Xem trước trực tiếp',
258
+ 'prompts.editor.edit': 'Chỉnh sửa',
259
+ 'prompts.editor.preview': 'Xem trước',
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
+ '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ê',
254
265
  'prompts.presets.title': 'Preset',
255
266
  'prompts.presets.hint': 'Quản lý preset.',
256
267
  'prompts.presets.namePlaceholder': 'Tên preset',
@@ -233,6 +233,17 @@ const zhTw = Object.freeze({
233
233
  'prompts.subTab.project': 'CLAUDE.md',
234
234
  'prompts.subTab.system': 'System Prompt (Pi)',
235
235
  'prompts.subTab.presets': '預設池',
236
+ 'prompts.editor.toolbar': 'Markdown 工具列',
237
+ 'prompts.editor.collapsePreview': '收起預覽',
238
+ 'prompts.editor.expandPreview': '展開預覽',
239
+ 'prompts.editor.previewToggle': '即時預覽',
240
+ 'prompts.editor.edit': '編輯',
241
+ 'prompts.editor.preview': '預覽',
242
+ 'prompts.editor.previewUnavailable': '預覽元件未載入(marked / DOMPurify 缺失),請重新整理頁面',
243
+ 'prompts.editor.previewEmpty': '尚無內容可預覽',
244
+ 'prompts.editor.changeAxis': '改動導覽軸',
245
+ 'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
246
+ 'prompts.editor.changeAxisTruncated': '改動區域過多,已摺疊為統計',
236
247
  'prompts.presets.title': '預設池',
237
248
  'prompts.presets.hint': '管理預設。',
238
249
  'prompts.presets.namePlaceholder': '預設名稱',
@@ -233,6 +233,17 @@ const zh = Object.freeze({
233
233
  'prompts.subTab.project': 'CLAUDE.md',
234
234
  'prompts.subTab.system': 'System Prompt (Pi)',
235
235
  'prompts.subTab.presets': '预设池',
236
+ 'prompts.editor.toolbar': 'Markdown 工具栏',
237
+ 'prompts.editor.collapsePreview': '收起预览',
238
+ 'prompts.editor.expandPreview': '展开预览',
239
+ 'prompts.editor.previewToggle': '实时预览',
240
+ 'prompts.editor.edit': '编辑',
241
+ 'prompts.editor.preview': '预览',
242
+ 'prompts.editor.previewUnavailable': '预览组件未加载(marked / DOMPurify 缺失),请刷新页面',
243
+ 'prompts.editor.previewEmpty': '暂无内容可预览',
244
+ 'prompts.editor.changeAxis': '改动导航轴',
245
+ 'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
246
+ 'prompts.editor.changeAxisTruncated': '改动区域过多,已折叠为统计',
236
247
  'prompts.presets.title': '预设池',
237
248
  'prompts.presets.hint': '管理预设。',
238
249
  'prompts.presets.namePlaceholder': '预设名称',