codexmate 0.0.44 → 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +20 -7
  2. package/README.zh.md +20 -7
  3. package/cli.js +473 -3
  4. package/package.json +2 -1
  5. package/web-ui/app.js +42 -5
  6. package/web-ui/index.html +2 -0
  7. package/web-ui/modules/app.computed.index.mjs +3 -1
  8. package/web-ui/modules/app.computed.main-tabs.mjs +3 -0
  9. package/web-ui/modules/app.computed.prompts.mjs +28 -0
  10. package/web-ui/modules/app.computed.session.mjs +23 -1
  11. package/web-ui/modules/app.constants.mjs +13 -0
  12. package/web-ui/modules/app.methods.agents.mjs +50 -4
  13. package/web-ui/modules/app.methods.index.mjs +6 -0
  14. package/web-ui/modules/app.methods.navigation.mjs +2 -1
  15. package/web-ui/modules/app.methods.opencode-config.mjs +228 -0
  16. package/web-ui/modules/app.methods.session-actions.mjs +10 -0
  17. package/web-ui/modules/app.methods.startup-claude.mjs +3 -1
  18. package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
  19. package/web-ui/modules/config-mode.computed.mjs +17 -1
  20. package/web-ui/modules/i18n/locales/en.mjs +66 -5
  21. package/web-ui/modules/i18n/locales/ja.mjs +66 -5
  22. package/web-ui/modules/i18n/locales/vi.mjs +74 -0
  23. package/web-ui/modules/i18n/locales/zh-tw.mjs +1269 -0
  24. package/web-ui/modules/i18n/locales/zh.mjs +66 -5
  25. package/web-ui/modules/i18n.dict.mjs +2 -0
  26. package/web-ui/modules/i18n.mjs +3 -2
  27. package/web-ui/partials/index/layout-footer.html +1 -1
  28. package/web-ui/partials/index/layout-header.html +70 -2
  29. package/web-ui/partials/index/modal-config-template-agents.html +12 -13
  30. package/web-ui/partials/index/panel-config-claude.html +6 -12
  31. package/web-ui/partials/index/panel-config-codex.html +6 -11
  32. package/web-ui/partials/index/panel-config-opencode.html +166 -0
  33. package/web-ui/partials/index/panel-prompts.html +100 -0
  34. package/web-ui/partials/index/panel-sessions.html +30 -10
  35. package/web-ui/partials/index/panel-usage.html +34 -18
  36. package/web-ui/res/web-ui-render.precompiled.js +932 -183
  37. package/web-ui/styles/controls-forms.css +62 -4
  38. package/web-ui/styles/modals-core.css +162 -0
  39. package/web-ui/styles/responsive.css +65 -5
  40. package/web-ui/styles/sessions-toolbar-trash.css +45 -10
  41. package/web-ui/styles/sessions-usage.css +31 -2
@@ -0,0 +1,100 @@
1
+ <!-- Prompts editor -->
2
+ <div
3
+ v-if="mainTab === 'prompts'"
4
+ class="mode-content mode-cards"
5
+ id="panel-prompts"
6
+ role="tabpanel"
7
+ aria-labelledby="tab-prompts">
8
+ <div class="segmented-control">
9
+ <button type="button" :class="['segment', { active: promptsSubTab === 'codex' }]" @click="switchPromptsSubTab('codex')">{{ t('prompts.subTab.codex') }}</button>
10
+ <button type="button" :class="['segment', { active: promptsSubTab === 'claude-md' }]" @click="switchPromptsSubTab('claude-md')">{{ t('prompts.subTab.claude') }}</button>
11
+ </div>
12
+
13
+ <div class="prompts-editor">
14
+ <div class="prompts-editor-toolbar">
15
+ <div class="form-hint">
16
+ {{ agentsPath || t('common.notLoaded') }}
17
+ <span v-if="agentsPath">
18
+ ({{ agentsExists ? t('common.exists') : t('common.notExistsWillCreateOnSave') }})
19
+ </span>
20
+ </div>
21
+ <div class="prompts-editor-actions">
22
+ <div class="prompts-editor-group prompts-editor-group--secondary">
23
+ <button
24
+ class="btn-mini"
25
+ @click="exportAgentsContent"
26
+ :disabled="agentsLoading">
27
+ {{ t('modal.agents.export') }}
28
+ </button>
29
+ <button
30
+ class="btn-mini"
31
+ @click="copyAgentsContent"
32
+ :disabled="agentsLoading">
33
+ {{ t('modal.agents.copy') }}
34
+ </button>
35
+ <button
36
+ class="btn-mini"
37
+ @click="pasteAgentsContent"
38
+ :disabled="agentsLoading || agentsSaving || agentsDiffVisible">
39
+ {{ t('common.paste') }}
40
+ </button>
41
+ </div>
42
+ <div class="prompts-editor-group prompts-editor-group--workflow">
43
+ <button class="btn-mini" @click="loadPromptsContent" :disabled="agentsSaving || agentsDiffLoading">{{ t('common.cancel') }}</button>
44
+ <button
45
+ v-if="agentsDiffVisible"
46
+ class="btn-mini"
47
+ @click="resetAgentsDiffState"
48
+ :disabled="agentsSaving || agentsDiffLoading">
49
+ {{ t('common.backToEdit') }}
50
+ </button>
51
+ <button class="btn-mini btn-confirm-mini" @click="applyAgentsContent" :disabled="agentsSaving || agentsLoading || agentsDiffLoading || (!agentsDiffVisible && !hasAgentsContentChanged()) || (agentsDiffVisible && !agentsDiffHasChanges)">
52
+ {{ agentsSaving ? (agentsDiffVisible ? t('common.saving') : t('common.previewing')) : (agentsDiffVisible ? t('common.save') : t('common.preview')) }}
53
+ </button>
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <div class="form-group">
59
+ <div v-if="agentsDiffVisible">
60
+ <div
61
+ v-if="!agentsDiffLoading && !agentsDiffError && !agentsDiffTruncated && (agentsDiffStats.added || agentsDiffStats.removed)"
62
+ class="agents-diff-summary">
63
+ <span class="agents-diff-stat add">+{{ agentsDiffStats.added }}</span>
64
+ <span class="agents-diff-stat del">-{{ agentsDiffStats.removed }}</span>
65
+ </div>
66
+ <div v-if="agentsDiffLoading" class="state-message">{{ t('diff.generating') }}</div>
67
+ <div v-else-if="agentsDiffError" class="state-message error">{{ agentsDiffError }}</div>
68
+ <div v-else-if="agentsDiffTruncated" class="agents-diff-empty">{{ t('diff.tooLargeSkip') }}</div>
69
+ <div v-else-if="!agentsDiffHasChanges" class="agents-diff-empty">{{ t('diff.noChanges') }}</div>
70
+ <div v-else class="agents-diff-view agents-diff-editor">
71
+ <div
72
+ v-for="(line, index) in agentsDiffLines"
73
+ :key="line.key || (line.type + '-' + index)"
74
+ :class="['agents-diff-line', line.type]">
75
+ <span class="agents-diff-line-sign">
76
+ {{ line.type === 'add' ? '+' : (line.type === 'del' ? '-' : ' ') }}
77
+ </span>
78
+ <span class="agents-diff-line-text">{{ line.value }}</span>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ <div :class="['editor-frame', { 'editor-frame--loading': agentsLoading }]">
83
+ <div v-if="agentsLoading" class="editor-skeleton">
84
+ <div class="skeleton-line" v-for="i in 6" :key="i"></div>
85
+ </div>
86
+ <textarea
87
+ v-model="agentsContent"
88
+ class="form-input template-editor"
89
+ spellcheck="false"
90
+ :readonly="agentsLoading || agentsSaving || agentsDiffVisible"
91
+ @input="onAgentsContentInput"
92
+ :placeholder="t(promptsSubTab === 'claude-md' ? 'modal.agents.placeholder.claudeMd' : 'modal.agents.placeholder')"></textarea>
93
+ </div>
94
+ <div v-if="promptsContextHint" :class="['prompts-context-hint', { 'prompts-context-hint--warn': promptsContextHint.warn }]">
95
+ {{ promptsContextHint.text }}
96
+ </div>
97
+ </div>
98
+
99
+ </div>
100
+ </div>
@@ -219,33 +219,53 @@
219
219
  </div>
220
220
  </div>
221
221
  <div class="session-actions">
222
- <button class="btn-session-refresh" @click="loadActiveSessionDetail" :disabled="sessionDetailLoading || !activeSession">
223
- {{ sessionDetailLoading ? t('sessions.preview.loading') : t('sessions.preview.refresh') }}
222
+ <button class="btn-session-refresh" @click="loadActiveSessionDetail" :disabled="sessionDetailLoading || !activeSession"
223
+ :title="sessionDetailLoading ? t('sessions.preview.loading') : t('sessions.preview.refresh')"
224
+ :aria-label="sessionDetailLoading ? t('sessions.preview.loading') : t('sessions.preview.refresh')">
225
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 8a5.5 5.5 0 0 1 9.4-3.8"/><path d="M13.5 8a5.5 5.5 0 0 1-9.4 3.8"/><polyline points="2.5 2 2.5 5 5.5 5"/><polyline points="13.5 14 13.5 11 10.5 11"/></svg>
224
226
  </button>
225
227
  <button
226
228
  v-if="isDeleteAvailable(activeSession)"
227
229
  class="btn-session-delete"
228
230
  @click="deleteSession(activeSession)"
229
- :disabled="!activeSession || sessionsLoading || sessionDeleting[getSessionExportKey(activeSession)]">
230
- {{ (activeSession && sessionDeleting[getSessionExportKey(activeSession)]) ? (sessionTrashEnabled === false ? t('sessions.preview.deleting') : t('sessions.preview.moving')) : (sessionTrashEnabled === false ? t('sessions.preview.deleteHard') : t('sessions.preview.moveToTrash')) }}
231
+ :disabled="!activeSession || sessionsLoading || sessionDeleting[getSessionExportKey(activeSession)]"
232
+ :title="(activeSession && sessionDeleting[getSessionExportKey(activeSession)]) ? (sessionTrashEnabled === false ? t('sessions.preview.deleting') : t('sessions.preview.moving')) : (sessionTrashEnabled === false ? t('sessions.preview.deleteHard') : t('sessions.preview.moveToTrash'))"
233
+ :aria-label="(activeSession && sessionDeleting[getSessionExportKey(activeSession)]) ? (sessionTrashEnabled === false ? t('sessions.preview.deleting') : t('sessions.preview.moving')) : (sessionTrashEnabled === false ? t('sessions.preview.deleteHard') : t('sessions.preview.moveToTrash'))">
234
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 4 4 4 13 4"/><path d="M5.5 4V2.5a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1V4"/><path d="M12 4v9.5a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 4 13.5V4"/></svg>
231
235
  </button>
232
236
  <button
233
237
  class="btn-session-export"
234
238
  @click="exportSession(activeSession)"
235
- :disabled="!activeSession || sessionExporting[getSessionExportKey(activeSession)]">
236
- {{ (activeSession && sessionExporting[getSessionExportKey(activeSession)]) ? t('sessions.preview.exporting') : t('sessions.preview.export') }}
239
+ :disabled="!activeSession || sessionExporting[getSessionExportKey(activeSession)]"
240
+ :title="(activeSession && sessionExporting[getSessionExportKey(activeSession)]) ? t('sessions.preview.exporting') : t('sessions.preview.export')"
241
+ :aria-label="(activeSession && sessionExporting[getSessionExportKey(activeSession)]) ? t('sessions.preview.exporting') : t('sessions.preview.export')">
242
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v8"/><polyline points="4 7 8 10.5 12 7"/><path d="M2.5 12v1.5a1 1 0 0 0 1 1h9a1 1 0 0 0 1-1V12"/></svg>
237
243
  </button>
244
+ <div class="session-link-group">
238
245
  <button
239
246
  class="btn-session-open"
240
247
  @click="copySessionLink(activeSession)"
241
- :disabled="!activeSession">
242
- {{ t('sessions.preview.copyLink') }}
248
+ :disabled="!activeSession || !canBuildStandaloneUrl(activeSession)"
249
+ :title="t('sessions.preview.copyLink')"
250
+ :aria-label="t('sessions.preview.copyLink')">
251
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="2.5" width="4" height="2" rx="1"/><path d="M4.5 4h7a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5"/><line x1="6" y1="7.5" x2="10" y2="7.5"/><line x1="6" y1="10" x2="10" y2="10"/></svg>
243
252
  </button>
253
+ <button
254
+ class="btn-session-open"
255
+ @click="openSessionLink(activeSession)"
256
+ :disabled="!activeSession || !canBuildStandaloneUrl(activeSession)"
257
+ :title="t('sessions.preview.openLink')"
258
+ :aria-label="t('sessions.preview.openLink')">
259
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2h-8.5A1.5 1.5 0 0 0 2 3.5v8A1.5 1.5 0 0 0 3.5 13h8a1.5 1.5 0 0 0 1.5-1.5v-3"/><path d="M13 2v4h-4"/><path d="M13 2L7 8"/></svg>
260
+ </button>
261
+ </div>
244
262
  <button
245
263
  class="btn-session-open"
246
264
  @click="copySessionPath(activeSession)"
247
- :disabled="!activeSession || !getSessionFilePath(activeSession)">
248
- {{ t('sessions.preview.copyPath') }}
265
+ :disabled="!activeSession || !getSessionFilePath(activeSession)"
266
+ :title="t('sessions.preview.copyPath')"
267
+ :aria-label="t('sessions.preview.copyPath')">
268
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="2.5" width="4" height="2" rx="1"/><path d="M4.5 4h7a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-7a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5"/><line x1="6" y1="7.5" x2="10" y2="7.5"/><line x1="6" y1="10" x2="10" y2="10"/></svg>
249
269
  </button>
250
270
  </div>
251
271
  </div>
@@ -75,26 +75,42 @@
75
75
  <section v-if="sessionUsageWave.points && sessionUsageWave.points.length" class="usage-wave-section">
76
76
  <div class="usage-card-title">{{ t('usage.daily.title') }}</div>
77
77
  <div class="usage-wave-container">
78
- <svg class="usage-wave-chart" viewBox="0 0 800 140" preserveAspectRatio="none">
79
- <defs>
80
- <linearGradient :id="'wave-gradient-' + sessionsUsageTimeRange" x1="0" y1="0" x2="0" y2="1">
81
- <stop offset="0%" :stop-color="'var(--color-brand)'" stop-opacity="0.35"/>
82
- <stop offset="100%" :stop-color="'var(--color-brand)'" stop-opacity="0"/>
83
- </linearGradient>
84
- </defs>
85
- <path :d="sessionUsageWave.areaPath" :fill="'url(#wave-gradient-' + sessionsUsageTimeRange + ')'" class="usage-wave-area"/>
86
- <path :d="sessionUsageWave.linePath" fill="none" :stroke="'var(--color-brand)'" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="usage-wave-line"/>
87
- <line v-if="sessionsUsageSelectedDay" x1="0" :x2="sessionUsageWave.width" :y1="sessionUsageWave.hoverY" :y2="sessionUsageWave.hoverY" stroke="currentColor" stroke-width="1" stroke-dasharray="4 4" opacity="0.5" class="usage-wave-hover-line"/>
88
- <circle v-if="sessionsUsageSelectedDay" :cx="sessionUsageWave.hoverX" :cy="sessionUsageWave.hoverY" r="5" :fill="'var(--color-surface)'" :stroke="'var(--color-brand)'" stroke-width="2.5" class="usage-wave-hover-point"/>
89
- </svg>
90
- <div class="usage-wave-labels">
91
- <span v-for="label in sessionUsageWave.labels" :key="label.key"
92
- class="usage-wave-label"
93
- :class="{ active: sessionsUsageSelectedDay === label.key }"
94
- @click="selectSessionsUsageDay(label.key)">
95
- {{ label.text }}
78
+ <div class="usage-wave-yaxis">
79
+ <span v-for="tick in sessionUsageWave.yTicks" :key="tick.value"
80
+ class="usage-wave-ytick"
81
+ :style="{ bottom: tick.percent + '%' }">
82
+ {{ tick.label }}
96
83
  </span>
97
84
  </div>
85
+ <div class="usage-wave-chart-area">
86
+ <svg class="usage-wave-chart" viewBox="0 0 800 140" preserveAspectRatio="none">
87
+ <defs>
88
+ <linearGradient :id="'wave-gradient-' + sessionsUsageTimeRange" x1="0" y1="0" x2="0" y2="1">
89
+ <stop offset="0%" :stop-color="'var(--color-brand)'" stop-opacity="0.35"/>
90
+ <stop offset="100%" :stop-color="'var(--color-brand)'" stop-opacity="0"/>
91
+ </linearGradient>
92
+ </defs>
93
+ <line v-for="tick in sessionUsageWave.yTicks" :key="'g-' + tick.value"
94
+ x1="0" :x2="sessionUsageWave.width"
95
+ :y1="tick.y" :y2="tick.y"
96
+ stroke="currentColor" stroke-width="0.5" opacity="0.12"
97
+ class="usage-wave-gridline"/>
98
+ <path :d="sessionUsageWave.areaPath" :fill="'url(#wave-gradient-' + sessionsUsageTimeRange + ')'" class="usage-wave-area"/>
99
+ <path :d="sessionUsageWave.linePath" fill="none" :stroke="'var(--color-brand)'" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="usage-wave-line"/>
100
+ <line v-if="sessionsUsageSelectedDay" x1="0" :x2="sessionUsageWave.width" :y1="sessionUsageWave.hoverY" :y2="sessionUsageWave.hoverY" stroke="currentColor" stroke-width="1" stroke-dasharray="4 4" opacity="0.5" class="usage-wave-hover-line"/>
101
+ <circle v-if="sessionsUsageSelectedDay" :cx="sessionUsageWave.hoverX" :cy="sessionUsageWave.hoverY" r="5" :fill="'var(--color-surface)'" :stroke="'var(--color-brand)'" stroke-width="2.5" class="usage-wave-hover-point"/>
102
+ </svg>
103
+ <div class="usage-wave-labels">
104
+ <button v-for="label in sessionUsageWave.labels" :key="label.key"
105
+ type="button"
106
+ class="usage-wave-label"
107
+ :class="{ active: sessionsUsageSelectedDay === label.key }"
108
+ :aria-pressed="sessionsUsageSelectedDay === label.key"
109
+ @click="selectSessionsUsageDay(label.key)">
110
+ {{ label.text }}
111
+ </button>
112
+ </div>
113
+ </div>
98
114
  </div>
99
115
  <div v-if="sessionsUsageSelectedDaySummary" class="usage-daydetail">
100
116
  <div class="usage-daydetail-header">