codexmate 0.1.23 → 0.1.25

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 (37) hide show
  1. package/cli.js +4 -1
  2. package/package.json +1 -1
  3. package/plugins/prompt-templates/computed.mjs +1 -1
  4. package/plugins/prompt-templates/index.mjs +3 -3
  5. package/plugins/prompt-templates/manifest.mjs +5 -3
  6. package/plugins/prompt-templates/methods.mjs +9 -2
  7. package/plugins/registry.mjs +5 -2
  8. package/plugins/text-tools/computed.mjs +23 -0
  9. package/plugins/text-tools/index.mjs +14 -0
  10. package/plugins/text-tools/manifest.mjs +20 -0
  11. package/plugins/text-tools/methods.mjs +24 -0
  12. package/plugins/text-tools/overview.mjs +12 -0
  13. package/plugins/text-tools/ownership.mjs +7 -0
  14. package/web-ui/app.js +5 -2
  15. package/web-ui/index.html +0 -1
  16. package/web-ui/logic.markdown-editor.mjs +5 -57
  17. package/web-ui/logic.prompts-change-axis.mjs +3 -88
  18. package/web-ui/logic.prompts-path-reference.mjs +118 -0
  19. package/web-ui/modules/app.methods.agents.mjs +1 -0
  20. package/web-ui/modules/app.methods.index.mjs +1 -1
  21. package/web-ui/modules/app.methods.navigation.mjs +79 -3
  22. package/web-ui/modules/app.methods.prompts-editor.mjs +140 -145
  23. package/web-ui/modules/app.methods.web-ui-preferences.mjs +7 -1
  24. package/web-ui/modules/i18n/locales/en.mjs +22 -1
  25. package/web-ui/modules/i18n/locales/ja.mjs +22 -1
  26. package/web-ui/modules/i18n/locales/vi.mjs +22 -1
  27. package/web-ui/modules/i18n/locales/zh-tw.mjs +22 -1
  28. package/web-ui/modules/i18n/locales/zh.mjs +22 -1
  29. package/web-ui/modules/plugins.computed.mjs +8 -2
  30. package/web-ui/modules/plugins.methods.mjs +8 -2
  31. package/web-ui/partials/index/layout-header.html +19 -4
  32. package/web-ui/partials/index/panel-plugins.html +45 -5
  33. package/web-ui/partials/index/panel-prompts.html +24 -31
  34. package/web-ui/res/web-ui-render.precompiled.js +142 -130
  35. package/web-ui/session-helpers.mjs +0 -1
  36. package/web-ui/source-bundle.cjs +1 -0
  37. package/web-ui/styles/prompts-editor.css +20 -91
@@ -2,6 +2,17 @@ const vi = Object.freeze({
2
2
  'plugins.catalog.promptTemplates.title': 'Mẫu prompt',
3
3
  'plugins.catalog.promptTemplates.description': 'Công cụ prompt theo mẫu, hỗ trợ biến, sao chép và xuất.',
4
4
  'plugins.status.standard': 'tiêu chuẩn',
5
+ 'plugins.catalog.textTools.title': 'Công cụ văn bản',
6
+ 'plugins.catalog.textTools.description': 'Bộ công cụ văn bản nhẹ; công cụ đầu tiên nén văn bản nhiều dòng thành một dòng.',
7
+ 'plugins.textTools.title': 'Công cụ văn bản',
8
+ 'plugins.textTools.tools.aria': 'Chọn công cụ văn bản',
9
+ 'plugins.textTools.tools.compress': 'Nén văn bản',
10
+ 'plugins.textTools.compress.inputLabel': 'Nhập văn bản',
11
+ 'plugins.textTools.compress.inputPlaceholder': 'Dán hoặc nhập văn bản cần nén…',
12
+ 'plugins.textTools.compress.inputAria': 'Văn bản cần nén',
13
+ 'plugins.textTools.compress.outputTitle': 'Kết quả nén',
14
+ 'plugins.textTools.compress.outputAria': 'Kết quả văn bản đã nén',
15
+ 'plugins.textTools.compress.copy': 'Sao chép',
5
16
  'plugins.builtin.commentPolish.name': 'Chỉnh chú thích mã',
6
17
  'plugins.builtin.commentPolish.desc': 'Chỉnh nhẹ các chú thích mã sau {{code}}',
7
18
  'plugins.builtin.commentPolish.line1': 'Chỉnh nhẹ các chú thích mã sau',
@@ -250,6 +261,17 @@ const vi = Object.freeze({
250
261
  'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
251
262
  'prompts.subTab.project': 'CLAUDE.md',
252
263
  'prompts.subTab.system': 'System Prompt (Pi)',
264
+ 'prompts.pathReference.title': 'Chèn tham chiếu quy tắc',
265
+ 'prompts.pathReference.placeholder': 'Chèn tham chiếu quy tắc…',
266
+ 'prompts.pathReference.codex': 'AGENTS.md (Codex)',
267
+ 'prompts.pathReference.opencode': 'AGENTS.md (OpenCode)',
268
+ 'prompts.pathReference.claudeGlobal': 'CLAUDE.md (Toàn cục)',
269
+ 'prompts.pathReference.claudeProject': 'CLAUDE.md (Dự án hiện tại)',
270
+ 'prompts.pathReference.system': '{file} (System Prompt Pi)',
271
+ 'prompts.pathReference.projectMissing': 'Chưa chọn đường dẫn dự án',
272
+ 'prompts.pathReference.notLoaded': 'Đang tải đường dẫn…',
273
+ 'prompts.pathReference.loadFailed': 'Tải đường dẫn thất bại',
274
+ 'prompts.pathReference.toast.inserted': 'Đã chèn tham chiếu quy tắc',
253
275
  'prompts.subTab.presets': 'Kho preset',
254
276
  'prompts.editor.toolbar': 'Thanh công cụ Markdown',
255
277
  'prompts.editor.collapsePreview': 'Thu gọn bản xem trước',
@@ -261,7 +283,6 @@ const vi = Object.freeze({
261
283
  'prompts.editor.previewEmpty': 'Chưa có nội dung để xem trước',
262
284
  'prompts.editor.changeAxis': 'Trục điều hướng thay đổi',
263
285
  '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ê',
265
286
  'prompts.presets.title': 'Preset',
266
287
  'prompts.presets.hint': 'Quản lý preset.',
267
288
  'prompts.presets.namePlaceholder': 'Tên preset',
@@ -232,6 +232,17 @@ const zhTw = Object.freeze({
232
232
  'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
233
233
  'prompts.subTab.project': 'CLAUDE.md',
234
234
  'prompts.subTab.system': 'System Prompt (Pi)',
235
+ 'prompts.pathReference.title': '插入規則引用',
236
+ 'prompts.pathReference.placeholder': '插入規則引用…',
237
+ 'prompts.pathReference.codex': 'AGENTS.md (Codex)',
238
+ 'prompts.pathReference.opencode': 'AGENTS.md (OpenCode)',
239
+ 'prompts.pathReference.claudeGlobal': 'CLAUDE.md(全域)',
240
+ 'prompts.pathReference.claudeProject': 'CLAUDE.md(目前專案)',
241
+ 'prompts.pathReference.system': '{file}(Pi System Prompt)',
242
+ 'prompts.pathReference.projectMissing': '未選擇專案路徑',
243
+ 'prompts.pathReference.notLoaded': '路徑載入中…',
244
+ 'prompts.pathReference.loadFailed': '路徑載入失敗',
245
+ 'prompts.pathReference.toast.inserted': '已插入規則引用',
235
246
  'prompts.subTab.presets': '預設池',
236
247
  'prompts.editor.toolbar': 'Markdown 工具列',
237
248
  'prompts.editor.collapsePreview': '收起預覽',
@@ -243,7 +254,6 @@ const zhTw = Object.freeze({
243
254
  'prompts.editor.previewEmpty': '尚無內容可預覽',
244
255
  'prompts.editor.changeAxis': '改動導覽軸',
245
256
  'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
246
- 'prompts.editor.changeAxisTruncated': '改動區域過多,已摺疊為統計',
247
257
  'prompts.presets.title': '預設池',
248
258
  'prompts.presets.hint': '管理預設。',
249
259
  'prompts.presets.namePlaceholder': '預設名稱',
@@ -374,6 +384,17 @@ const zhTw = Object.freeze({
374
384
  'plugins.catalog.promptTemplates.title': '提示詞模板',
375
385
  'plugins.catalog.promptTemplates.description': '標準化、模板驅動的提示詞工具,支持變量、複製與匯出。',
376
386
  'plugins.status.standard': '標準',
387
+ 'plugins.catalog.textTools.title': '文字工具',
388
+ 'plugins.catalog.textTools.description': '一組輕量純文字小工具,首個工具可將多行文字壓縮為單行。',
389
+ 'plugins.textTools.title': '文字工具',
390
+ 'plugins.textTools.tools.aria': '文字工具選擇',
391
+ 'plugins.textTools.tools.compress': '壓縮文字',
392
+ 'plugins.textTools.compress.inputLabel': '輸入文字',
393
+ 'plugins.textTools.compress.inputPlaceholder': '貼上或輸入需要壓縮的文字…',
394
+ 'plugins.textTools.compress.inputAria': '輸入需要壓縮的文字',
395
+ 'plugins.textTools.compress.outputTitle': '壓縮結果',
396
+ 'plugins.textTools.compress.outputAria': '壓縮後的文字結果',
397
+ 'plugins.textTools.compress.copy': '複製',
377
398
  'plugins.main.ariaWorkspace': '插件工作區',
378
399
  'plugins.refresh': '重新整理',
379
400
  'plugins.refreshing': '重新整理中...',
@@ -232,6 +232,17 @@ const zh = Object.freeze({
232
232
  'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
233
233
  'prompts.subTab.project': 'CLAUDE.md',
234
234
  'prompts.subTab.system': 'System Prompt (Pi)',
235
+ 'prompts.pathReference.title': '插入规则引用',
236
+ 'prompts.pathReference.placeholder': '插入规则引用…',
237
+ 'prompts.pathReference.codex': 'AGENTS.md (Codex)',
238
+ 'prompts.pathReference.opencode': 'AGENTS.md (OpenCode)',
239
+ 'prompts.pathReference.claudeGlobal': 'CLAUDE.md(全局)',
240
+ 'prompts.pathReference.claudeProject': 'CLAUDE.md(当前项目)',
241
+ 'prompts.pathReference.system': '{file}(Pi System Prompt)',
242
+ 'prompts.pathReference.projectMissing': '未选择项目路径',
243
+ 'prompts.pathReference.notLoaded': '路径加载中…',
244
+ 'prompts.pathReference.loadFailed': '路径加载失败',
245
+ 'prompts.pathReference.toast.inserted': '已插入规则引用',
235
246
  'prompts.subTab.presets': '预设池',
236
247
  'prompts.editor.toolbar': 'Markdown 工具栏',
237
248
  'prompts.editor.collapsePreview': '收起预览',
@@ -243,7 +254,6 @@ const zh = Object.freeze({
243
254
  'prompts.editor.previewEmpty': '暂无内容可预览',
244
255
  'prompts.editor.changeAxis': '改动导航轴',
245
256
  'prompts.editor.changeAxisTick': '第 {line} 行(+{added} / -{removed})',
246
- 'prompts.editor.changeAxisTruncated': '改动区域过多,已折叠为统计',
247
257
  'prompts.presets.title': '预设池',
248
258
  'prompts.presets.hint': '管理预设。',
249
259
  'prompts.presets.namePlaceholder': '预设名称',
@@ -374,6 +384,17 @@ const zh = Object.freeze({
374
384
  'plugins.catalog.promptTemplates.title': '提示词模板',
375
385
  'plugins.catalog.promptTemplates.description': '标准化、模板驱动的提示词工具,支持变量、复制与导出。',
376
386
  'plugins.status.standard': '标准',
387
+ 'plugins.catalog.textTools.title': '文本工具',
388
+ 'plugins.catalog.textTools.description': '一组轻量纯文本小工具,首个工具可将多行文本压缩为单行。',
389
+ 'plugins.textTools.title': '文本工具',
390
+ 'plugins.textTools.tools.aria': '文本工具选择',
391
+ 'plugins.textTools.tools.compress': '压缩文本',
392
+ 'plugins.textTools.compress.inputLabel': '输入文本',
393
+ 'plugins.textTools.compress.inputPlaceholder': '粘贴或输入需要压缩的文本…',
394
+ 'plugins.textTools.compress.inputAria': '输入需要压缩的文本',
395
+ 'plugins.textTools.compress.outputTitle': '压缩结果',
396
+ 'plugins.textTools.compress.outputAria': '压缩后的文本结果',
397
+ 'plugins.textTools.compress.copy': '复制',
377
398
  'plugins.main.ariaWorkspace': '插件工作区',
378
399
  'plugins.refresh': '刷新',
379
400
  'plugins.refreshing': '刷新中...',
@@ -1,3 +1,9 @@
1
- import { createPluginsComputed } from '../../plugins/prompt-templates/computed.mjs';
1
+ import { createPromptTemplatesComputed } from '../../plugins/prompt-templates/computed.mjs';
2
+ import { createTextToolsComputed } from '../../plugins/text-tools/computed.mjs';
2
3
 
3
- export { createPluginsComputed };
4
+ export function createPluginsComputed() {
5
+ return {
6
+ ...createPromptTemplatesComputed(),
7
+ ...createTextToolsComputed()
8
+ };
9
+ }
@@ -1,3 +1,9 @@
1
- import { createPluginsMethods } from '../../plugins/prompt-templates/methods.mjs';
1
+ import { createPromptTemplatesMethods } from '../../plugins/prompt-templates/methods.mjs';
2
+ import { createTextToolsMethods } from '../../plugins/text-tools/methods.mjs';
2
3
 
3
- export { createPluginsMethods };
4
+ export function createPluginsMethods() {
5
+ return {
6
+ ...createPromptTemplatesMethods(),
7
+ ...createTextToolsMethods()
8
+ };
9
+ }
@@ -346,10 +346,11 @@
346
346
  <button
347
347
  id="side-tab-plugins"
348
348
  data-main-tab="plugins"
349
- :aria-current="mainTab === 'plugins' ? 'page' : null"
350
- :class="['side-item', { active: isMainTabNavActive('plugins') }]"
351
- @pointerdown="onMainTabPointerDown('plugins', $event)"
352
- @click="onMainTabClick('plugins', $event)">
349
+ data-plugins-id="prompt-templates"
350
+ :aria-current="mainTab === 'plugins' && pluginsActiveId === 'prompt-templates' ? 'page' : null"
351
+ :class="['side-item', { active: isPluginsIdNavActive('prompt-templates') }]"
352
+ @pointerdown="onPluginsTabPointerDown('prompt-templates', $event)"
353
+ @click="onPluginsTabClick('prompt-templates', $event)">
353
354
  <span class="side-item-icon" aria-hidden="true">◇</span>
354
355
  <div class="side-item-title">{{ t('side.plugins.tools') }}</div>
355
356
  <div class="side-item-meta">
@@ -357,6 +358,20 @@
357
358
  <span>{{ t('side.plugins.templatesCount', { count: promptTemplatesList.length }) }}</span>
358
359
  </div>
359
360
  </button>
361
+ <button
362
+ id="side-tab-plugins-text-tools"
363
+ data-main-tab="plugins"
364
+ data-plugins-id="text-tools"
365
+ :aria-current="mainTab === 'plugins' && pluginsActiveId === 'text-tools' ? 'page' : null"
366
+ :class="['side-item', { active: isPluginsIdNavActive('text-tools') }]"
367
+ @pointerdown="onPluginsTabPointerDown('text-tools', $event)"
368
+ @click="onPluginsTabClick('text-tools', $event)">
369
+ <span class="side-item-icon" aria-hidden="true">▤</span>
370
+ <div class="side-item-title">{{ t('plugins.catalog.textTools.title') }}</div>
371
+ <div class="side-item-meta">
372
+ <span>{{ t('plugins.textTools.tools.compress') }}</span>
373
+ </div>
374
+ </button>
360
375
  </div>
361
376
 
362
377
  <div class="side-section" role="navigation" :aria-label="t('side.system')">
@@ -11,11 +11,6 @@
11
11
  <span class="selector-title">{{ t('plugins.sidebar.title') }}</span>
12
12
  <div class="plugins-panel-note">{{ t('plugins.sidebar.note') }}</div>
13
13
  </div>
14
- <div class="settings-tab-actions">
15
- <button type="button" class="btn-tool btn-tool-compact" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">
16
- {{ pluginsLoading ? t('plugins.refreshing') : t('plugins.refresh') }}
17
- </button>
18
- </div>
19
14
  </div>
20
15
 
21
16
  <div class="plugins-list" role="list">
@@ -239,6 +234,51 @@
239
234
  </div>
240
235
  </div>
241
236
  </div>
237
+ <div v-else-if="pluginsActiveId === 'text-tools'" class="plugins-panel">
238
+ <div class="plugins-panel-head">
239
+ <div>
240
+ <div class="plugins-panel-title">{{ t('plugins.textTools.title') }}</div>
241
+ <div v-if="pluginsActiveAttribution" class="plugins-panel-note">{{ pluginsActiveAttribution }}</div>
242
+ </div>
243
+ </div>
244
+
245
+ <div class="prompt-templates-modebar" role="tablist" :aria-label="t('plugins.textTools.tools.aria')">
246
+ <button
247
+ v-for="tool in textToolsList"
248
+ :key="'text-tool-' + tool.id"
249
+ type="button"
250
+ role="tab"
251
+ :aria-selected="textToolsActiveToolId === tool.id"
252
+ :class="['mode-pill', { active: textToolsActiveToolId === tool.id }]"
253
+ @click="textToolsActiveToolId = tool.id">{{ tool.label }}</button>
254
+ </div>
255
+
256
+ <div class="prompt-compose">
257
+ <div class="prompt-compose-workspace">
258
+ <div class="prompt-compose-main">
259
+ <div class="prompt-compose-form">
260
+ <label class="form-label">{{ t('plugins.textTools.compress.inputLabel') }}</label>
261
+ <textarea
262
+ class="form-input prompt-editor-textarea"
263
+ v-model="textToolsInput"
264
+ rows="10"
265
+ spellcheck="false"
266
+ :placeholder="t('plugins.textTools.compress.inputPlaceholder')"
267
+ :aria-label="t('plugins.textTools.compress.inputAria')"></textarea>
268
+ </div>
269
+ </div>
270
+ <div class="prompt-preview-block prompt-compose-preview">
271
+ <div class="prompt-vars-head">
272
+ <div>
273
+ <div class="prompt-vars-title">{{ t('plugins.textTools.compress.outputTitle') }}</div>
274
+ </div>
275
+ <button type="button" class="btn-mini" @click="copyTextToolsOutput" :disabled="!textToolsOutput">{{ t('plugins.textTools.compress.copy') }}</button>
276
+ </div>
277
+ <textarea class="form-input prompt-preview-textarea" :value="textToolsOutput" rows="10" readonly spellcheck="false" :aria-label="t('plugins.textTools.compress.outputAria')"></textarea>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ </div>
242
282
 
243
283
  <div v-else class="skills-empty-state">{{ t('plugins.promptTemplates.noPluginSelected') }}</div>
244
284
  </section>
@@ -44,6 +44,17 @@
44
44
  </span>
45
45
  </div>
46
46
  <div class="prompts-editor-actions">
47
+ <div class="prompts-editor-group prompts-editor-group--pathref">
48
+ <select
49
+ class="form-input prompts-path-reference-select"
50
+ :disabled="agentsLoading || agentsSaving || agentsDiffVisible || promptsPathReferenceLoading"
51
+ :title="t('prompts.pathReference.title')"
52
+ @focus="loadPromptsPathReferences()"
53
+ @change="insertPromptPathReference($event.target.value, 'agents', $event)">
54
+ <option value="">{{ t('prompts.pathReference.placeholder') }}</option>
55
+ <option v-for="opt in promptsPathReferenceOptionList()" :key="opt.id" :value="opt.id" :disabled="opt.disabled">{{ opt.label }}</option>
56
+ </select>
57
+ </div>
47
58
  <div class="prompts-editor-group prompts-editor-group--secondary">
48
59
  <button
49
60
  class="btn-mini"
@@ -75,17 +86,9 @@
75
86
  </button>
76
87
  </div>
77
88
  <div class="prompts-editor-group prompts-editor-group--workflow">
78
- <button class="btn-mini" @click="loadPromptsContent" :disabled="agentsSaving || agentsDiffLoading" :title="t('common.cancel')">
89
+ <button class="btn-mini" @click="agentsDiffVisible ? resetAgentsDiffState() : loadPromptsContent()" :disabled="agentsSaving || agentsDiffLoading" :title="t('common.cancel')">
79
90
  <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
80
91
  </button>
81
- <button
82
- v-if="agentsDiffVisible"
83
- class="btn-mini"
84
- @click="resetAgentsDiffState"
85
- :disabled="agentsSaving || agentsDiffLoading"
86
- :title="t('common.backToEdit')">
87
- <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
88
- </button>
89
92
  <button class="btn-mini btn-confirm-mini" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (!agentsDiffVisible && !hasAgentsContentChanged()) || (agentsDiffVisible && !agentsDiffHasChanges)" :title="agentsDiffVisible ? t('common.save') : t('common.preview')">
90
93
  <svg v-if="agentsDiffVisible" class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>
91
94
  <svg v-else class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
@@ -223,7 +226,6 @@
223
226
  </div>
224
227
  <div class="prompts-editor-columns">
225
228
  <div class="prompts-edit-pane">
226
- <pre ref="promptsAgentsHighlight" class="prompts-highlight-backdrop" aria-hidden="true" v-html="agentsHighlightHtml"></pre>
227
229
  <textarea
228
230
  ref="promptsAgentsTextarea"
229
231
  v-model="agentsContent"
@@ -231,15 +233,7 @@
231
233
  spellcheck="false"
232
234
  :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
233
235
  @input="onAgentsContentInput"
234
- @scroll="syncPromptsOverlayScroll('agents')"
235
236
  :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>
243
237
  </div>
244
238
  <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
245
239
  <div v-if="promptsPreviewLibsMissing" class="state-message error">{{ t('prompts.editor.previewUnavailable') }}</div>
@@ -282,6 +276,17 @@
282
276
  </span>
283
277
  </div>
284
278
  <div class="prompts-editor-actions">
279
+ <div class="prompts-editor-group prompts-editor-group--pathref">
280
+ <select
281
+ class="form-input prompts-path-reference-select"
282
+ :disabled="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible || promptsPathReferenceLoading"
283
+ :title="t('prompts.pathReference.title')"
284
+ @focus="loadPromptsPathReferences()"
285
+ @change="insertPromptPathReference($event.target.value, 'sys', $event)">
286
+ <option value="">{{ t('prompts.pathReference.placeholder') }}</option>
287
+ <option v-for="opt in promptsPathReferenceOptionList()" :key="opt.id" :value="opt.id" :disabled="opt.disabled">{{ opt.label }}</option>
288
+ </select>
289
+ </div>
285
290
  <div class="prompts-editor-group prompts-editor-group--secondary">
286
291
  <button class="btn-mini" @click="exportSysPromptContent" :disabled="sysPromptLoading || sysPromptSaving || !sysPromptContent" :title="t('modal.agents.export')">
287
292
  <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v12M8 11l4 4 4-4M4 17v2h16v-2"/></svg>
@@ -297,12 +302,9 @@
297
302
  </button>
298
303
  </div>
299
304
  <div class="prompts-editor-group prompts-editor-group--workflow">
300
- <button class="btn-mini" @click="cancelSysPromptEdit" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.cancel')">
305
+ <button class="btn-mini" @click="sysPromptDiffVisible ? resetSysPromptDiffState() : cancelSysPromptEdit()" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.cancel')">
301
306
  <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
302
307
  </button>
303
- <button v-if="sysPromptDiffVisible" class="btn-mini" @click="resetSysPromptDiffState" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.backToEdit')">
304
- <svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
305
- </button>
306
308
  <button class="btn-mini btn-confirm-mini" @click="applySystemPrompt" :disabled="sysPromptSaving || sysPromptLoading || sysPromptDiffLoading || (!sysPromptDiffVisible && !hasSysPromptContentChanged()) || (sysPromptDiffVisible && !sysPromptDiffHasChangesValue)" :title="sysPromptDiffVisible ? t('common.save') : t('common.preview')">
307
309
  <svg v-if="sysPromptDiffVisible" class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 6L9 17l-5-5"/></svg>
308
310
  <svg v-else class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
@@ -377,22 +379,13 @@
377
379
  </div>
378
380
  <div class="prompts-editor-columns">
379
381
  <div class="prompts-edit-pane">
380
- <pre ref="promptsSysHighlight" class="prompts-highlight-backdrop" aria-hidden="true" v-html="sysPromptHighlightHtml"></pre>
381
382
  <textarea
382
383
  ref="promptsSysTextarea"
383
384
  v-model="sysPromptContent"
384
385
  class="form-input template-editor"
385
386
  spellcheck="false"
386
387
  :readonly="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible"
387
- @scroll="syncPromptsOverlayScroll('sys')"
388
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>
396
389
  </div>
397
390
  <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
398
391
  <div v-if="promptsPreviewLibsMissing" class="state-message error">{{ t('prompts.editor.previewUnavailable') }}</div>