codexmate 0.1.20 → 0.1.22

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.
@@ -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
  },
@@ -63,6 +63,7 @@ function normalizePromptsSubTab(value) {
63
63
  const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
64
64
  if (normalized === 'claude-project') return normalized;
65
65
  if (normalized === 'system') return normalized;
66
+ if (normalized === 'opencode') return normalized;
66
67
  return 'codex';
67
68
  }
68
69
 
@@ -229,9 +229,18 @@ const en = Object.freeze({
229
229
  'subtitle.settings': 'Manage downloads, directories, and trash.',
230
230
  'subtitle.prompts': 'Edit AGENTS.md and CLAUDE.md.',
231
231
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
232
+ 'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
232
233
  'prompts.subTab.project': 'CLAUDE.md',
233
234
  'prompts.subTab.system': 'System Prompt (Pi)',
234
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',
235
244
  'prompts.presets.title': 'Presets',
236
245
  'prompts.presets.hint': 'Manage presets.',
237
246
  'prompts.presets.namePlaceholder': 'Preset name',
@@ -439,6 +448,7 @@ const en = Object.freeze({
439
448
  'toast.agents.saved.agents': 'AGENTS.md saved',
440
449
  'toast.agents.saved.claudeProject': 'Project CLAUDE.md saved',
441
450
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md saved',
451
+ 'toast.agents.saved.opencode': 'OpenCode AGENTS.md saved',
442
452
  'toast.agents.saved.workspace': 'Workspace file saved: {name}',
443
453
  'toast.delete.ok': 'Deleted',
444
454
  'toast.delete.fail': 'Delete failed',
@@ -961,6 +971,7 @@ const en = Object.freeze({
961
971
  'config.agents.open': 'Open AGENTS.md',
962
972
  'modal.agents.title.default': 'AGENTS.md editor',
963
973
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md editor',
974
+ 'modal.agents.title.opencode': 'OpenCode AGENTS.md editor',
964
975
  'modal.agents.hint.default': 'Saved content will be written to AGENTS.md (next to config.toml).',
965
976
  'modal.agents.title.claudeProject': 'Project CLAUDE.md: {path}',
966
977
  'modal.agents.hint.claudeProject': 'Saved content will be written to CLAUDE.md in: {path}',
@@ -969,6 +980,7 @@ const en = Object.freeze({
969
980
  'modal.agents.contentLabel.claudeProject': 'CLAUDE.md content',
970
981
  'modal.agents.placeholder.claudeProject': 'Edit CLAUDE.md here',
971
982
  'modal.agents.hint.openclaw': 'Saved content will be written to OpenClaw workspace AGENTS.md.',
983
+ 'modal.agents.hint.opencode': 'Saved content will be written to ~/.config/opencode/AGENTS.md.',
972
984
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw workspace file: {fileName}',
973
985
  'modal.agents.hint.openclawWorkspaceFile': 'Saved content will be written to OpenClaw workspace {fileName}.',
974
986
  'config.url.unset': 'URL not set',
@@ -230,9 +230,18 @@ const ja = Object.freeze({
230
230
  'subtitle.settings': 'ダウンロード・ディレクトリ・ゴミ箱の管理。',
231
231
  'subtitle.prompts': 'AGENTS.md と CLAUDE.md を編集。',
232
232
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
233
+ 'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
233
234
  'prompts.subTab.project': 'CLAUDE.md',
234
235
  'prompts.subTab.system': 'System Prompt (Pi)',
235
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': 'プレビューする内容がありません',
236
245
  'prompts.presets.title': 'プリセット',
237
246
  'prompts.presets.hint': 'プリセットを管理します。',
238
247
  'prompts.presets.namePlaceholder': 'プリセット名',
@@ -441,6 +450,7 @@ const ja = Object.freeze({
441
450
  'toast.agents.saved.agents': 'AGENTS.md 保存しました',
442
451
  'toast.agents.saved.claudeProject': 'プロジェクト CLAUDE.md 保存しました',
443
452
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 保存しました',
453
+ 'toast.agents.saved.opencode': 'OpenCode AGENTS.md 保存しました',
444
454
  'toast.agents.saved.workspace': 'ワークスペースファイル保存済み: {name}',
445
455
  'toast.delete.ok': '削除しました',
446
456
  'toast.delete.fail': '削除に失敗しました',
@@ -964,6 +974,7 @@ const ja = Object.freeze({
964
974
  'config.agents.open': 'AGENTS.md を開く',
965
975
  'modal.agents.title.default': 'AGENTS.md エディタ',
966
976
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md エディタ',
977
+ 'modal.agents.title.opencode': 'OpenCode AGENTS.md エディタ',
967
978
  'modal.agents.hint.default': '保存すると対象の AGENTS.md(config.toml と同階層)に書き込まれます。',
968
979
  'modal.agents.title.claudeProject': 'プロジェクト CLAUDE.md: {path}',
969
980
  'modal.agents.hint.claudeProject': '保存するとプロジェクトの CLAUDE.md に書き込まれます: {path}',
@@ -972,6 +983,7 @@ const ja = Object.freeze({
972
983
  'modal.agents.contentLabel.claudeProject': 'CLAUDE.md 内容',
973
984
  'modal.agents.placeholder.claudeProject': 'CLAUDE.md を編集',
974
985
  'modal.agents.hint.openclaw': '保存すると OpenClaw Workspace の AGENTS.md に書き込まれます。',
986
+ 'modal.agents.hint.opencode': '保存すると ~/.config/opencode/AGENTS.md に書き込まれます。',
975
987
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw ワークスペースファイル: {fileName}',
976
988
  'modal.agents.hint.openclawWorkspaceFile': '保存すると OpenClaw Workspace の {fileName} に書き込まれます。',
977
989
  'config.url.unset': 'URL 未設定',
@@ -247,9 +247,18 @@ const vi = Object.freeze({
247
247
  'subtitle.settings': 'Quản lý tải xuống, thư mục và dữ liệu.',
248
248
  'subtitle.prompts': 'Chỉnh sửa AGENTS.md và CLAUDE.md.',
249
249
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
250
+ 'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
250
251
  'prompts.subTab.project': 'CLAUDE.md',
251
252
  'prompts.subTab.system': 'System Prompt (Pi)',
252
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',
253
262
  'prompts.presets.title': 'Preset',
254
263
  'prompts.presets.hint': 'Quản lý preset.',
255
264
  'prompts.presets.namePlaceholder': 'Tên preset',
@@ -401,6 +410,7 @@ const vi = Object.freeze({
401
410
  'toast.agents.saved.agents': 'AGENTS.md đã lưu',
402
411
  'toast.agents.saved.claudeProject': 'CLAUDE.md dự án đã lưu',
403
412
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md đã lưu',
413
+ 'toast.agents.saved.opencode': 'OpenCode AGENTS.md đã lưu',
404
414
  'toast.agents.saved.workspace': 'Tệp workspace đã lưu: {name}',
405
415
  'toast.delete.ok': 'Đã xóa',
406
416
  'toast.delete.fail': 'Xóa thất bại',
@@ -1073,8 +1083,10 @@ const vi = Object.freeze({
1073
1083
  'config.agents.open': 'Mở AGENTS.md',
1074
1084
  'modal.agents.title.default': 'Trình soạn thảo AGENTS.md',
1075
1085
  'modal.agents.title.openclaw': 'Trình soạn thảo AGENTS.md OpenClaw',
1086
+ 'modal.agents.title.opencode': 'Trình soạn thảo AGENTS.md OpenCode',
1076
1087
  'modal.agents.hint.default': 'Nội dung đã lưu sẽ được ghi vào AGENTS.md (cạnh config.toml).',
1077
1088
  'modal.agents.hint.openclaw': 'Nội dung đã lưu sẽ được ghi vào AGENTS.md không gian làm việc OpenClaw.',
1089
+ 'modal.agents.hint.opencode': 'Nội dung đã lưu sẽ được ghi vào ~/.config/opencode/AGENTS.md.',
1078
1090
  'modal.agents.title.openclawWorkspaceFile': 'File không gian làm việc OpenClaw: {fileName}',
1079
1091
  'modal.agents.hint.openclawWorkspaceFile': 'Nội dung đã lưu sẽ được ghi vào OpenClaw {fileName}.',
1080
1092
  'config.url.unset': 'URL chưa đặt',
@@ -229,9 +229,18 @@ const zhTw = Object.freeze({
229
229
  'subtitle.settings': '管理下載、目錄與回收站。',
230
230
  'subtitle.prompts': '編輯 AGENTS.md 與 CLAUDE.md。',
231
231
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
232
+ 'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
232
233
  'prompts.subTab.project': 'CLAUDE.md',
233
234
  'prompts.subTab.system': 'System Prompt (Pi)',
234
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': '尚無內容可預覽',
235
244
  'prompts.presets.title': '預設池',
236
245
  'prompts.presets.hint': '管理預設。',
237
246
  'prompts.presets.namePlaceholder': '預設名稱',
@@ -439,6 +448,7 @@ const zhTw = Object.freeze({
439
448
  'toast.agents.saved.agents': 'AGENTS.md 已保存',
440
449
  'toast.agents.saved.claudeProject': '專案 CLAUDE.md 已保存',
441
450
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 已保存',
451
+ 'toast.agents.saved.opencode': 'OpenCode AGENTS.md 已保存',
442
452
  'toast.agents.saved.workspace': '工作區文件已保存: {name}',
443
453
  'toast.delete.ok': '已刪除',
444
454
  'toast.delete.fail': '刪除失敗',
@@ -960,6 +970,7 @@ const zhTw = Object.freeze({
960
970
  'config.agents.open': '打開 AGENTS.md',
961
971
  'modal.agents.title.default': 'AGENTS.md 編輯器',
962
972
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md 編輯器',
973
+ 'modal.agents.title.opencode': 'OpenCode AGENTS.md 編輯器',
963
974
  'modal.agents.hint.default': '保存後會寫入目標 AGENTS.md(與 config.toml 同級)。',
964
975
  'modal.agents.title.claudeProject': '專案 CLAUDE.md: {path}',
965
976
  'modal.agents.hint.claudeProject': '保存後會寫入專案目錄下的 CLAUDE.md: {path}',
@@ -968,6 +979,7 @@ const zhTw = Object.freeze({
968
979
  'modal.agents.contentLabel.claudeProject': 'CLAUDE.md 內容',
969
980
  'modal.agents.placeholder.claudeProject': '在這裡編輯 CLAUDE.md',
970
981
  'modal.agents.hint.openclaw': '保存後會寫入 OpenClaw Workspace 下的 AGENTS.md。',
982
+ 'modal.agents.hint.opencode': '儲存後會寫入 ~/.config/opencode/AGENTS.md。',
971
983
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw 工作區文件: {fileName}',
972
984
  'modal.agents.hint.openclawWorkspaceFile': '保存後會寫入 OpenClaw Workspace 下的 {fileName}。',
973
985
  'config.url.unset': '未設 URL',
@@ -229,9 +229,18 @@ const zh = Object.freeze({
229
229
  'subtitle.settings': '管理下载、目录与回收站。',
230
230
  'subtitle.prompts': '编辑 AGENTS.md 与 CLAUDE.md。',
231
231
  'prompts.subTab.codex': 'AGENTS.md (Codex)',
232
+ 'prompts.subTab.opencode': 'AGENTS.md (OpenCode)',
232
233
  'prompts.subTab.project': 'CLAUDE.md',
233
234
  'prompts.subTab.system': 'System Prompt (Pi)',
234
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': '暂无内容可预览',
235
244
  'prompts.presets.title': '预设池',
236
245
  'prompts.presets.hint': '管理预设。',
237
246
  'prompts.presets.namePlaceholder': '预设名称',
@@ -439,6 +448,7 @@ const zh = Object.freeze({
439
448
  'toast.agents.saved.agents': 'AGENTS.md 已保存',
440
449
  'toast.agents.saved.claudeProject': '项目 CLAUDE.md 已保存',
441
450
  'toast.agents.saved.openclaw': 'OpenClaw AGENTS.md 已保存',
451
+ 'toast.agents.saved.opencode': 'OpenCode AGENTS.md 已保存',
442
452
  'toast.agents.saved.workspace': '工作区文件已保存: {name}',
443
453
  'toast.delete.ok': '已删除',
444
454
  'toast.delete.fail': '删除失败',
@@ -960,6 +970,7 @@ const zh = Object.freeze({
960
970
  'config.agents.open': '打开 AGENTS.md',
961
971
  'modal.agents.title.default': 'AGENTS.md 编辑器',
962
972
  'modal.agents.title.openclaw': 'OpenClaw AGENTS.md 编辑器',
973
+ 'modal.agents.title.opencode': 'OpenCode AGENTS.md 编辑器',
963
974
  'modal.agents.hint.default': '保存后会写入目标 AGENTS.md(与 config.toml 同级)。',
964
975
  'modal.agents.title.claudeProject': '项目 CLAUDE.md: {path}',
965
976
  'modal.agents.hint.claudeProject': '保存后会写入项目目录下的 CLAUDE.md: {path}',
@@ -968,6 +979,7 @@ const zh = Object.freeze({
968
979
  'modal.agents.contentLabel.claudeProject': 'CLAUDE.md 内容',
969
980
  'modal.agents.placeholder.claudeProject': '在这里编辑 CLAUDE.md',
970
981
  'modal.agents.hint.openclaw': 'Workspace / AGENTS.md',
982
+ 'modal.agents.hint.opencode': '保存后会写入 ~/.config/opencode/AGENTS.md。',
971
983
  'modal.agents.title.openclawWorkspaceFile': 'OpenClaw 工作区文件: {fileName}',
972
984
  'modal.agents.hint.openclawWorkspaceFile': 'Workspace / {fileName}',
973
985
  'config.url.unset': '未设 URL',
@@ -7,6 +7,7 @@
7
7
  aria-labelledby="tab-prompts">
8
8
  <div class="prompts-md-tabs" role="tablist" :aria-label="t('tab.prompts')">
9
9
  <button type="button" :class="['prompts-md-tab', { active: promptsSubTab === 'codex' }]" role="tab" :aria-selected="promptsSubTab === 'codex'" @click="switchPromptsSubTab('codex')">{{ t('prompts.subTab.codex') }}</button>
10
+ <button type="button" :class="['prompts-md-tab', { active: promptsSubTab === 'opencode' }]" role="tab" :aria-selected="promptsSubTab === 'opencode'" @click="switchPromptsSubTab('opencode')">{{ t('prompts.subTab.opencode') }}</button>
10
11
  <button type="button" :class="['prompts-md-tab', { active: promptsSubTab === 'claude-project' }]" role="tab" :aria-selected="promptsSubTab === 'claude-project'" @click="switchPromptsSubTab('claude-project')">{{ t('prompts.subTab.project') }}</button>
11
12
  <button type="button" :class="['prompts-md-tab', { active: promptsSubTab === 'system' }]" role="tab" :aria-selected="promptsSubTab === 'system'" @click="switchPromptsSubTab('system')">{{ t('prompts.subTab.system') }}</button>
12
13
  </div>
@@ -200,17 +201,40 @@
200
201
  </div>
201
202
  </div>
202
203
  </div>
203
- <div :class="['editor-frame', { 'editor-frame--loading': agentsLoading }]">
204
+ <div class="prompts-md-toolbar" role="toolbar" :aria-label="t('prompts.editor.toolbar')">
205
+ <div class="prompts-md-toolbar-group prompts-md-toolbar-group--view">
206
+ <div v-if="promptsPreviewEnabled" class="prompts-mobile-view-switch" role="tablist" :aria-label="t('prompts.editor.previewToggle')">
207
+ <button type="button" role="tab" :aria-selected="promptsMobileView === 'edit'" :class="['prompts-mobile-view-btn', { active: promptsMobileView === 'edit' }]" @click="setPromptsMobileView('edit')">{{ t('prompts.editor.edit') }}</button>
208
+ <button type="button" role="tab" :aria-selected="promptsMobileView === 'preview'" :class="['prompts-mobile-view-btn', { active: promptsMobileView === 'preview' }]" @click="setPromptsMobileView('preview')">{{ t('prompts.editor.preview') }}</button>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ <div :class="['editor-frame prompts-editor-frame', { 'editor-frame--loading': agentsLoading, 'prompts-editor-frame--split': promptsPreviewEnabled, 'prompts-editor-frame--preview-collapsed': promptsPreviewCollapsed, 'prompts-editor-frame--mobile-preview': promptsPreviewEnabled && promptsMobileView === 'preview' }]">
213
+ <button v-if="promptsPreviewEnabled" type="button" class="prompts-split-collapse-btn" :class="{ 'prompts-split-collapse-btn--collapsed': promptsPreviewCollapsed }" :title="promptsPreviewCollapsed ? t('prompts.editor.expandPreview') : t('prompts.editor.collapsePreview')" :aria-label="promptsPreviewCollapsed ? t('prompts.editor.expandPreview') : t('prompts.editor.collapsePreview')" @click="togglePromptsPreviewCollapsed()">
214
+ <svg class="prompts-split-collapse-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 6l6 6-6 6"/></svg>
215
+ </button>
204
216
  <div v-if="agentsLoading" class="editor-skeleton">
205
217
  <div class="skeleton-line" v-for="i in 6" :key="i"></div>
206
218
  </div>
207
- <textarea
208
- v-model="agentsContent"
209
- class="form-input template-editor"
210
- spellcheck="false"
211
- :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
212
- @input="onAgentsContentInput"
213
- :placeholder="t(promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')"></textarea>
219
+ <div class="prompts-editor-columns">
220
+ <div class="prompts-edit-pane">
221
+ <pre ref="promptsAgentsHighlight" class="prompts-highlight-backdrop" aria-hidden="true" v-html="agentsHighlightHtml"></pre>
222
+ <textarea
223
+ ref="promptsAgentsTextarea"
224
+ v-model="agentsContent"
225
+ class="form-input template-editor"
226
+ spellcheck="false"
227
+ :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
228
+ @input="onAgentsContentInput"
229
+ @scroll="syncPromptsOverlayScroll('agents')"
230
+ :placeholder="t(promptsSubTab === 'claude-project' ? 'modal.agents.placeholder.claudeProject' : 'modal.agents.placeholder')"></textarea>
231
+ </div>
232
+ <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
233
+ <div v-if="promptsPreviewLibsMissing" class="state-message error">{{ t('prompts.editor.previewUnavailable') }}</div>
234
+ <div v-else-if="!agentsPreviewHtml" class="state-message">{{ t('prompts.editor.previewEmpty') }}</div>
235
+ <div v-else class="prompts-preview-body" v-html="agentsPreviewHtml"></div>
236
+ </div>
237
+ </div>
214
238
  </div>
215
239
  <div v-if="promptsContextHint" :class="['prompts-context-hint', { 'prompts-context-hint--warn': promptsContextHint.warn }]">
216
240
  {{ promptsContextHint.text }}
@@ -319,16 +343,39 @@
319
343
  </div>
320
344
  </div>
321
345
  </div>
322
- <div :class="['editor-frame', { 'editor-frame--loading': sysPromptLoading }]">
346
+ <div class="prompts-md-toolbar" role="toolbar" :aria-label="t('prompts.editor.toolbar')">
347
+ <div class="prompts-md-toolbar-group prompts-md-toolbar-group--view">
348
+ <div v-if="promptsPreviewEnabled" class="prompts-mobile-view-switch" role="tablist" :aria-label="t('prompts.editor.previewToggle')">
349
+ <button type="button" role="tab" :aria-selected="promptsMobileView === 'edit'" :class="['prompts-mobile-view-btn', { active: promptsMobileView === 'edit' }]" @click="setPromptsMobileView('edit')">{{ t('prompts.editor.edit') }}</button>
350
+ <button type="button" role="tab" :aria-selected="promptsMobileView === 'preview'" :class="['prompts-mobile-view-btn', { active: promptsMobileView === 'preview' }]" @click="setPromptsMobileView('preview')">{{ t('prompts.editor.preview') }}</button>
351
+ </div>
352
+ </div>
353
+ </div>
354
+ <div :class="['editor-frame prompts-editor-frame', { 'editor-frame--loading': sysPromptLoading, 'prompts-editor-frame--split': promptsPreviewEnabled, 'prompts-editor-frame--preview-collapsed': promptsPreviewCollapsed, 'prompts-editor-frame--mobile-preview': promptsPreviewEnabled && promptsMobileView === 'preview' }]">
355
+ <button v-if="promptsPreviewEnabled" type="button" class="prompts-split-collapse-btn" :class="{ 'prompts-split-collapse-btn--collapsed': promptsPreviewCollapsed }" :title="promptsPreviewCollapsed ? t('prompts.editor.expandPreview') : t('prompts.editor.collapsePreview')" :aria-label="promptsPreviewCollapsed ? t('prompts.editor.expandPreview') : t('prompts.editor.collapsePreview')" @click="togglePromptsPreviewCollapsed()">
356
+ <svg class="prompts-split-collapse-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 6l6 6-6 6"/></svg>
357
+ </button>
323
358
  <div v-if="sysPromptLoading" class="editor-skeleton">
324
359
  <div class="skeleton-line" v-for="i in 6" :key="i"></div>
325
360
  </div>
326
- <textarea
327
- v-model="sysPromptContent"
328
- class="form-input template-editor"
329
- spellcheck="false"
330
- :readonly="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible"
331
- :placeholder="t('sysPrompt.placeholder')"></textarea>
361
+ <div class="prompts-editor-columns">
362
+ <div class="prompts-edit-pane">
363
+ <pre ref="promptsSysHighlight" class="prompts-highlight-backdrop" aria-hidden="true" v-html="sysPromptHighlightHtml"></pre>
364
+ <textarea
365
+ ref="promptsSysTextarea"
366
+ v-model="sysPromptContent"
367
+ class="form-input template-editor"
368
+ spellcheck="false"
369
+ :readonly="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible"
370
+ @scroll="syncPromptsOverlayScroll('sys')"
371
+ :placeholder="t('sysPrompt.placeholder')"></textarea>
372
+ </div>
373
+ <div v-if="promptsPreviewEnabled" class="prompts-preview-pane">
374
+ <div v-if="promptsPreviewLibsMissing" class="state-message error">{{ t('prompts.editor.previewUnavailable') }}</div>
375
+ <div v-else-if="!sysPromptPreviewHtml" class="state-message">{{ t('prompts.editor.previewEmpty') }}</div>
376
+ <div v-else class="prompts-preview-body" v-html="sysPromptPreviewHtml"></div>
377
+ </div>
378
+ </div>
332
379
  </div>
333
380
  <div v-if="sysPromptContextHint()" :class="['prompts-context-hint', { 'prompts-context-hint--warn': sysPromptContextHint().warn }]">
334
381
  {{ sysPromptContextHint().text }}