codexmate 0.1.10 → 0.1.13
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 +5 -7
- package/README.vi.md +4 -7
- package/README.zh.md +5 -7
- package/cli/agents-files.js +27 -3
- package/cli/prompt-history.js +171 -0
- package/cli/system-prompt-files.js +20 -2
- package/cli.js +162 -5
- package/package.json +1 -1
- package/web-ui/app.js +95 -4
- package/web-ui/index.html +2 -1
- package/web-ui/modules/app.methods.agents.mjs +90 -0
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.navigation.mjs +7 -1
- package/web-ui/modules/app.methods.pi-config.mjs +371 -0
- package/web-ui/modules/app.methods.session-actions.mjs +57 -0
- package/web-ui/modules/app.methods.startup-claude.mjs +2 -1
- package/web-ui/modules/app.methods.system-prompt.mjs +81 -0
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +8 -1
- package/web-ui/modules/config-mode.computed.mjs +2 -1
- package/web-ui/modules/i18n/locales/en.mjs +52 -0
- package/web-ui/modules/i18n/locales/ja.mjs +52 -0
- package/web-ui/modules/i18n/locales/vi.mjs +52 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +52 -0
- package/web-ui/modules/i18n/locales/zh.mjs +52 -0
- package/web-ui/partials/index/layout-header.html +20 -0
- package/web-ui/partials/index/panel-config-claude.html +3 -3
- package/web-ui/partials/index/panel-config-codex.html +3 -3
- package/web-ui/partials/index/panel-config-kilocode.html +5 -5
- package/web-ui/partials/index/panel-config-openclaw.html +3 -3
- package/web-ui/partials/index/panel-config-opencode.html +5 -5
- package/web-ui/partials/index/panel-config-pi.html +171 -0
- package/web-ui/partials/index/panel-prompts.html +52 -0
- package/web-ui/partials/index/panel-sessions.html +12 -0
- package/web-ui/partials/index/panel-settings.html +42 -0
- package/web-ui/res/web-ui-render.precompiled.js +1101 -252
- package/web-ui/styles/modals-core.css +86 -0
- package/web-ui/styles/sessions-preview.css +38 -0
- package/web-ui/styles/settings-panel.css +7 -0
- package/web-ui/styles/titles-cards.css +2 -2
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
role="tabpanel"
|
|
7
7
|
:aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-opencode'">
|
|
8
8
|
<div v-if="forceCompactLayout && !sessionStandalone" class="segmented-control">
|
|
9
|
-
<button type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
|
|
10
|
-
<button type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
|
|
11
|
-
<button type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
|
|
12
|
-
<button type="button" :class="['segment', { active: configMode === 'opencode' }]" @click="switchConfigMode('opencode')">{{ t('tab.config.opencode') }}</button>
|
|
13
|
-
<button type="button" :class="['segment', { active: configMode === 'kilocode' }]" @click="switchConfigMode('kilocode')">{{ t('tab.config.kilocode') }}</button>
|
|
9
|
+
<button v-if="isConfigModeVisible('codex')" type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
|
|
10
|
+
<button v-if="isConfigModeVisible('claude')" type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
|
|
11
|
+
<button v-if="isConfigModeVisible('openclaw')" type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
|
|
12
|
+
<button v-if="isConfigModeVisible('opencode')" type="button" :class="['segment', { active: configMode === 'opencode' }]" @click="switchConfigMode('opencode')">{{ t('tab.config.opencode') }}</button>
|
|
13
|
+
<button v-if="isConfigModeVisible('kilocode')" type="button" :class="['segment', { active: configMode === 'kilocode' }]" @click="switchConfigMode('kilocode')">{{ t('tab.config.kilocode') }}</button>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
<section class="tool-config-write-card" :aria-label="t('opencode.writeAria')">
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<!-- Pi 配置 -->
|
|
2
|
+
<div
|
|
3
|
+
v-show="mainTab === 'config' && configMode === 'pi'"
|
|
4
|
+
class="mode-content mode-cards"
|
|
5
|
+
id="panel-config-pi"
|
|
6
|
+
role="tabpanel"
|
|
7
|
+
:aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-pi'">
|
|
8
|
+
<div v-if="forceCompactLayout && !sessionStandalone" class="segmented-control">
|
|
9
|
+
<button v-if="isConfigModeVisible('codex')" type="button" :class="['segment', { active: configMode === 'codex' }]" @click="switchConfigMode('codex')">{{ t('tab.config.codex') }}</button>
|
|
10
|
+
<button v-if="isConfigModeVisible('claude')" type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
|
|
11
|
+
<button v-if="isConfigModeVisible('openclaw')" type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
|
|
12
|
+
<button v-if="isConfigModeVisible('opencode')" type="button" :class="['segment', { active: configMode === 'opencode' }]" @click="switchConfigMode('opencode')">{{ t('tab.config.opencode') }}</button>
|
|
13
|
+
<button v-if="isConfigModeVisible('kilocode')" type="button" :class="['segment', { active: configMode === 'kilocode' }]" @click="switchConfigMode('kilocode')">{{ t('tab.config.kilocode') }}</button>
|
|
14
|
+
<button v-if="isConfigModeVisible('pi')" type="button" :class="['segment', { active: configMode === 'pi' }]" @click="switchConfigMode('pi')">{{ t('tab.config.pi') }}</button>
|
|
15
|
+
</div>
|
|
16
|
+
<section class="tool-config-write-card" :aria-label="t('toolConfig.pi.title')">
|
|
17
|
+
<div class="tool-config-write-copy">
|
|
18
|
+
<div class="tool-config-write-title">{{ t('toolConfig.pi.title') }}</div>
|
|
19
|
+
<p class="tool-config-write-desc">{{ t('toolConfig.pi.desc') }}</p>
|
|
20
|
+
</div>
|
|
21
|
+
<label class="settings-toggle-row tool-config-write-toggle">
|
|
22
|
+
<input
|
|
23
|
+
type="checkbox"
|
|
24
|
+
autocomplete="off"
|
|
25
|
+
:checked="isToolConfigWriteAllowed('pi')"
|
|
26
|
+
:disabled="toolConfigPermissionSaving.pi"
|
|
27
|
+
@change="setToolConfigPermission('pi', $event.target.checked)">
|
|
28
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
29
|
+
<span>{{ toolConfigPermissionStatusLabel('pi') }}</span>
|
|
30
|
+
</label>
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<div class="tool-config-write-scope" :class="{ locked: !isToolConfigWriteAllowed('pi') }">
|
|
34
|
+
<div class="tool-config-write-body">
|
|
35
|
+
<section class="selector-section">
|
|
36
|
+
<div class="selector-header">
|
|
37
|
+
<span class="selector-title">{{ t('pi.providers.title') }}</span>
|
|
38
|
+
<button type="button" class="btn-tool" @click="startAddPiProvider" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.add') }}</button>
|
|
39
|
+
</div>
|
|
40
|
+
<div class="config-template-hint">{{ t('pi.providers.targetFile', { path: '~/.pi/agent/models.json' }) }}</div>
|
|
41
|
+
<div class="card-list" v-if="piProviderIds.length">
|
|
42
|
+
<div v-for="providerId in piProviderIds" :key="providerId" class="card" role="button" tabindex="0" @click="openEditPiProvider(providerId)">
|
|
43
|
+
<div class="card-leading">
|
|
44
|
+
<div class="card-icon">{{ piProviderName(providerId).charAt(0).toUpperCase() }}</div>
|
|
45
|
+
<div class="card-content">
|
|
46
|
+
<div class="card-title">{{ piProviderName(providerId) }}</div>
|
|
47
|
+
<div class="card-subtitle">{{ piProviderSummary(providerId) }}</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="card-trailing">
|
|
51
|
+
<span v-if="piActiveProvider === providerId" class="pill configured">{{ t('pi.providers.active') }}</span>
|
|
52
|
+
<button v-else type="button" class="btn-tool" @click.stop="switchPiActiveProvider(providerId)" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.setActive') }}</button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div v-if="!piProviderIds.length" class="config-template-hint">{{ t('pi.providers.empty') }}</div>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<section class="selector-section" v-if="editingPiProvider">
|
|
60
|
+
<div class="selector-header">
|
|
61
|
+
<span class="selector-title">{{ t('pi.providers.editorTitle') }}</span>
|
|
62
|
+
<div class="settings-tab-actions">
|
|
63
|
+
<button type="button" class="btn-tool" @click="savePiProvider" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('common.save') }}</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="codex-config-grid">
|
|
67
|
+
<div class="form-group codex-config-field">
|
|
68
|
+
<label class="form-label">{{ t('pi.providers.fields.name') }}</label>
|
|
69
|
+
<input class="form-input" type="text" :value="piProviderName(editingPiProvider.id)" readonly>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="form-group codex-config-field">
|
|
72
|
+
<label class="form-label">{{ t('pi.providers.fields.baseUrl') }}</label>
|
|
73
|
+
<input class="form-input" type="url" v-model="editingPiProvider.form.baseUrl" autocomplete="off" spellcheck="false" placeholder="https://">
|
|
74
|
+
</div>
|
|
75
|
+
<div class="form-group codex-config-field">
|
|
76
|
+
<label class="form-label">{{ t('pi.providers.fields.api') }}</label>
|
|
77
|
+
<select class="form-input" v-model="editingPiProvider.form.api" :disabled="!isToolConfigWriteAllowed('pi')">
|
|
78
|
+
<option value="openai-completions">openai-completions — {{ t('pi.providers.addModal.api.openaiCompletions') }}</option>
|
|
79
|
+
<option value="openai-responses">openai-responses — {{ t('pi.providers.addModal.api.openaiResponses') }}</option>
|
|
80
|
+
<option value="anthropic-messages">anthropic-messages — {{ t('pi.providers.addModal.api.anthropicMessages') }}</option>
|
|
81
|
+
<option value="google-generative-ai">google-generative-ai — {{ t('pi.providers.addModal.api.googleGenerativeAi') }}</option>
|
|
82
|
+
<option v-if="!['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai'].includes(editingPiProvider.form.api)" :value="editingPiProvider.form.api">{{ editingPiProvider.form.api || '—' }}</option>
|
|
83
|
+
</select>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="form-group codex-config-field">
|
|
86
|
+
<label class="form-label">{{ t('pi.providers.fields.apiKey') }}</label>
|
|
87
|
+
<div class="input-with-toggle">
|
|
88
|
+
<input class="form-input" v-model="editingPiProvider.form.apiKey" :type="piShowKey ? 'text' : 'password'" autocomplete="off" spellcheck="false" placeholder="sk-...">
|
|
89
|
+
<button type="button" class="input-toggle-btn" @mousedown.prevent @click="piShowKey = !piShowKey" :title="piShowKey ? t('common.hide') : t('common.show')" :aria-label="piShowKey ? t('common.hide') : t('common.show')">
|
|
90
|
+
<svg v-if="!piShowKey" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" width="16" height="16"><path d="M10 4C5 4 1.73 8.11 1 10c.73 1.89 4 6 9 6s8.27-4.11 9-6c-.73-1.89-4-6-9-6z"/><circle cx="10" cy="10" r="3"/></svg>
|
|
91
|
+
<svg v-else viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" width="16" height="16"><path d="M2 2l16 16M8.2 4.2A9.9 9.9 0 0 1 10 4c5 0 8.27 4.11 9 6-.44.94-1.5 2.7-3.2 4.2M14.5 14.5A5.9 5.9 0 0 1 10 16c-5 0-8.27-4.11-9-6 .76-1.66 2.2-3.6 4.3-5"/></svg>
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="form-group">
|
|
97
|
+
<label class="form-label">{{ t('pi.providers.fields.models') }}</label>
|
|
98
|
+
<p v-if="piRemoteModelsLoading" class="config-template-hint">{{ t('pi.providers.models.remoteLoading') }}</p>
|
|
99
|
+
<p v-else-if="piRemoteModelError" class="config-template-hint">{{ piRemoteModelError }} · {{ t('pi.providers.models.remoteFailed') }}</p>
|
|
100
|
+
<div class="card-list">
|
|
101
|
+
<div class="card" v-for="(model, index) in editingPiProvider.form.models" :key="index">
|
|
102
|
+
<div class="input-with-toggle">
|
|
103
|
+
<input class="form-input" type="text" v-model="model.id" autocomplete="off" spellcheck="false" placeholder="claude-sonnet-4" :disabled="!isToolConfigWriteAllowed('pi')" @input="piModelSearch = model.id">
|
|
104
|
+
<button type="button" class="btn-tool input-toggle-btn" @click="editingPiProvider.form.models.splice(index, 1)" :disabled="!isToolConfigWriteAllowed('pi')">{{ t('pi.providers.models.remove') }}</button>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="card" v-if="editingPiProvider.form.models.length === 0">
|
|
108
|
+
<input class="form-input" type="text" v-model="piModelSearch" autocomplete="off" spellcheck="false" :placeholder="t('pi.providers.models.search')" @keydown.enter.prevent="addPiRemoteModel(piModelSearch.trim())">
|
|
109
|
+
</div>
|
|
110
|
+
<div class="card" v-if="!piRemoteModelsLoading && !piRemoteModelError && piModelSearch.trim() && piFilteredRemoteModels().length > 0">
|
|
111
|
+
<button type="button" class="btn-tool" style="width: 100%; text-align: left; justify-content: flex-start;" v-for="id in piFilteredRemoteModels()" :key="id" @click="addPiRemoteModel(id)">{{ id }}</button>
|
|
112
|
+
<p class="config-template-hint">{{ t('pi.providers.models.remoteCount', { count: piRemoteModels.length }) }}</p>
|
|
113
|
+
</div>
|
|
114
|
+
<p v-if="!piRemoteModelsLoading && !piRemoteModelError && piModelSearch.trim() && piFilteredRemoteModels().length === 0" class="config-template-hint">{{ t('pi.providers.models.noMatch') }}</p>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="settings-tab-actions">
|
|
118
|
+
<button type="button" class="btn-tool" @click="confirmDeletePiProvider" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.delete') }}</button>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
121
|
+
|
|
122
|
+
<section class="selector-section" v-if="showAddPiProviderModal">
|
|
123
|
+
<div class="selector-header">
|
|
124
|
+
<span class="selector-title">{{ t('pi.providers.addModal.title') }}</span>
|
|
125
|
+
<div class="settings-tab-actions">
|
|
126
|
+
<button type="button" class="btn-tool" @click="addPiProviderFromModal" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('common.save') }}</button>
|
|
127
|
+
<button type="button" class="btn-tool" @click="cancelAddPiProviderModal">{{ t('common.cancel') }}</button>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="config-template-hint">{{ t('pi.providers.addModal.subtitle') }}</div>
|
|
131
|
+
<div class="codex-config-grid">
|
|
132
|
+
<div class="form-group codex-config-field">
|
|
133
|
+
<label class="form-label">{{ t('pi.providers.addModal.fields.baseUrl') }}</label>
|
|
134
|
+
<input class="form-input" type="url" v-model="addingPiProviderBaseUrl" autocomplete="off" spellcheck="false" placeholder="https://" @input="onAddingPiEndpointChange">
|
|
135
|
+
</div>
|
|
136
|
+
<div class="form-group codex-config-field">
|
|
137
|
+
<label class="form-label">{{ t('pi.providers.addModal.fields.api') }}</label>
|
|
138
|
+
<select class="form-input" v-model="addingPiProviderApi">
|
|
139
|
+
<option value="openai-completions">openai-completions — {{ t('pi.providers.addModal.api.openaiCompletions') }}</option>
|
|
140
|
+
<option value="openai-responses">openai-responses — {{ t('pi.providers.addModal.api.openaiResponses') }}</option>
|
|
141
|
+
<option value="anthropic-messages">anthropic-messages — {{ t('pi.providers.addModal.api.anthropicMessages') }}</option>
|
|
142
|
+
<option value="google-generative-ai">google-generative-ai — {{ t('pi.providers.addModal.api.googleGenerativeAi') }}</option>
|
|
143
|
+
</select>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="form-group codex-config-field">
|
|
146
|
+
<label class="form-label">{{ t('pi.providers.addModal.fields.apiKey') }}</label>
|
|
147
|
+
<input class="form-input" type="password" v-model="addingPiProviderApiKey" autocomplete="off" spellcheck="false" :placeholder="t('common.optional')" @input="onAddingPiEndpointChange">
|
|
148
|
+
</div>
|
|
149
|
+
<div class="form-group codex-config-field">
|
|
150
|
+
<label class="form-label">{{ t('pi.providers.fields.models') }}</label>
|
|
151
|
+
<p v-if="addingPiRemoteLoading" class="config-template-hint">{{ t('pi.providers.models.remoteLoading') }}</p>
|
|
152
|
+
<p v-else-if="addingPiRemoteError" class="config-template-hint">{{ addingPiRemoteError }} · {{ t('pi.providers.models.remoteFailed') }}</p>
|
|
153
|
+
<input class="form-input" type="text" v-model="addingPiProviderModel" autocomplete="off" spellcheck="false" :placeholder="addingPiRemoteModels.length ? t('pi.providers.models.search') : 'claude-sonnet-4'">
|
|
154
|
+
<template v-if="!addingPiRemoteLoading && addingPiProviderModel.trim() && piFilteredAddingRemoteModels().length > 0">
|
|
155
|
+
<button type="button" class="btn-tool" style="width: 100%; text-align: left; justify-content: flex-start; margin-top: 8px;" v-for="id in piFilteredAddingRemoteModels()" :key="id" @click="addingPiProviderModel = id">{{ id }}</button>
|
|
156
|
+
<p class="config-template-hint">{{ t('pi.providers.models.remoteCount', { count: addingPiRemoteModels.length }) }}</p>
|
|
157
|
+
</template>
|
|
158
|
+
<p v-else-if="!addingPiRemoteLoading && !addingPiRemoteError && addingPiProviderModel.trim() && addingPiRemoteModels.length > 0" class="config-template-hint">{{ t('pi.providers.models.noMatch') }}</p>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
<div v-if="!isToolConfigWriteAllowed('pi')" class="tool-config-write-overlay">
|
|
163
|
+
<div class="tool-config-write-overlay-card">
|
|
164
|
+
<div class="tool-config-write-overlay-title">{{ t('toolConfig.pi.lockedTitle') }}</div>
|
|
165
|
+
<p>{{ t('toolConfig.pi.lockedDesc') }}</p>
|
|
166
|
+
<button type="button" class="btn-tool" @click="setToolConfigPermission('pi', true)" :disabled="toolConfigPermissionSaving.pi">{{ t('toolConfig.enableWrite') }}</button>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
@@ -65,6 +65,13 @@
|
|
|
65
65
|
:title="t('common.paste')">
|
|
66
66
|
<svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"/></svg>
|
|
67
67
|
</button>
|
|
68
|
+
<button
|
|
69
|
+
class="btn-mini"
|
|
70
|
+
@click="openPromptHistory"
|
|
71
|
+
:disabled="agentsSaving || agentsDiffVisible"
|
|
72
|
+
:title="t('common.history')">
|
|
73
|
+
<svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 8v4l3 2"/><path d="M3 12a9 9 0 1 0 9-9 9 9 0 0 0-7.5 4M3 4v4h4"/></svg>
|
|
74
|
+
</button>
|
|
68
75
|
</div>
|
|
69
76
|
<div class="prompts-editor-group prompts-editor-group--workflow">
|
|
70
77
|
<button class="btn-mini" @click="loadPromptsContent" :disabled="agentsSaving || agentsDiffLoading" :title="t('common.cancel')">
|
|
@@ -85,6 +92,27 @@
|
|
|
85
92
|
</div>
|
|
86
93
|
</div>
|
|
87
94
|
</div>
|
|
95
|
+
<details v-if="promptHistoryVisible" class="prompt-history-panel" open>
|
|
96
|
+
<summary class="prompt-history-summary">
|
|
97
|
+
<span>{{ t('common.history') }}</span>
|
|
98
|
+
<button type="button" class="btn-mini prompt-history-close" @click="closePromptHistory" :title="t('common.close')">✕</button>
|
|
99
|
+
</summary>
|
|
100
|
+
<div class="prompt-history-body">
|
|
101
|
+
<ul v-if="!promptHistoryLoading && !promptHistoryError && promptHistoryItems.length" class="prompt-history-list">
|
|
102
|
+
<li v-for="item in promptHistoryItems" :key="item.id" :class="['prompt-history-item', { active: promptHistoryPreviewId === item.id }]" @click="viewPromptHistoryItem(item)">
|
|
103
|
+
<span class="prompt-history-time">{{ item.id }}</span>
|
|
104
|
+
<span class="prompt-history-size">{{ Math.round(item.size / 1024 * 10) / 10 }} KB</span>
|
|
105
|
+
</li>
|
|
106
|
+
</ul>
|
|
107
|
+
<div v-if="promptHistoryLoading" class="state-message">{{ t('diff.generating') }}</div>
|
|
108
|
+
<div v-else-if="promptHistoryError" class="state-message error">{{ promptHistoryError }}</div>
|
|
109
|
+
<div v-else-if="!promptHistoryItems.length" class="state-message">{{ t('toast.history.empty') }}</div>
|
|
110
|
+
<div v-if="promptHistoryPreviewContent" class="prompt-history-preview-wrap">
|
|
111
|
+
<pre class="prompt-history-preview">{{ promptHistoryPreviewContent }}</pre>
|
|
112
|
+
<button type="button" class="btn-mini btn-confirm-mini" @click="applyPromptHistoryToEditor" :disabled="agentsSaving || agentsDiffVisible">{{ t('common.restoreToEditor') }}</button>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
</details>
|
|
88
116
|
<div class="prompt-presets-inline-row">
|
|
89
117
|
<div class="prompt-presets-inline-group">
|
|
90
118
|
<label class="prompt-presets-inline-label">{{ t('prompts.presets.title') }}</label>
|
|
@@ -228,6 +256,9 @@
|
|
|
228
256
|
<button class="btn-mini" @click="pasteSysPromptContent" :disabled="sysPromptLoading || sysPromptSaving || sysPromptDiffVisible" :title="t('common.paste')">
|
|
229
257
|
<svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"/></svg>
|
|
230
258
|
</button>
|
|
259
|
+
<button class="btn-mini" @click="openSysHistory" :disabled="sysPromptSaving || sysPromptDiffVisible" :title="t('common.history')">
|
|
260
|
+
<svg class="btn-icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 8v4l3 2"/><path d="M3 12a9 9 0 1 0 9-9 9 9 0 0 0-7.5 4M3 4v4h4"/></svg>
|
|
261
|
+
</button>
|
|
231
262
|
</div>
|
|
232
263
|
<div class="prompts-editor-group prompts-editor-group--workflow">
|
|
233
264
|
<button class="btn-mini" @click="cancelSysPromptEdit" :disabled="sysPromptSaving || sysPromptDiffLoading" :title="t('common.cancel')">
|
|
@@ -243,6 +274,27 @@
|
|
|
243
274
|
</div>
|
|
244
275
|
</div>
|
|
245
276
|
</div>
|
|
277
|
+
<details v-if="sysHistoryVisible" class="prompt-history-panel" open>
|
|
278
|
+
<summary class="prompt-history-summary">
|
|
279
|
+
<span>{{ t('common.history') }}</span>
|
|
280
|
+
<button type="button" class="btn-mini prompt-history-close" @click="closeSysHistory" :title="t('common.close')">✕</button>
|
|
281
|
+
</summary>
|
|
282
|
+
<div class="prompt-history-body">
|
|
283
|
+
<ul v-if="!sysHistoryLoading && !sysHistoryError && sysHistoryItems.length" class="prompt-history-list">
|
|
284
|
+
<li v-for="item in sysHistoryItems" :key="item.id" :class="['prompt-history-item', { active: sysHistoryPreviewId === item.id }]" @click="viewSysHistoryItem(item)">
|
|
285
|
+
<span class="prompt-history-time">{{ item.id }}</span>
|
|
286
|
+
<span class="prompt-history-size">{{ Math.round(item.size / 1024 * 10) / 10 }} KB</span>
|
|
287
|
+
</li>
|
|
288
|
+
</ul>
|
|
289
|
+
<div v-if="sysHistoryLoading" class="state-message">{{ t('diff.generating') }}</div>
|
|
290
|
+
<div v-else-if="sysHistoryError" class="state-message error">{{ sysHistoryError }}</div>
|
|
291
|
+
<div v-else-if="!sysHistoryItems.length" class="state-message">{{ t('toast.history.empty') }}</div>
|
|
292
|
+
<div v-if="sysHistoryPreviewContent" class="prompt-history-preview-wrap">
|
|
293
|
+
<pre class="prompt-history-preview">{{ sysHistoryPreviewContent }}</pre>
|
|
294
|
+
<button type="button" class="btn-mini btn-confirm-mini" @click="applySysHistoryToEditor" :disabled="sysPromptSaving || sysPromptDiffVisible">{{ t('common.restoreToEditor') }}</button>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</details>
|
|
246
298
|
<div class="form-group">
|
|
247
299
|
<div v-if="sysPromptDiffVisible">
|
|
248
300
|
<div
|
|
@@ -410,6 +410,18 @@
|
|
|
410
410
|
</div>
|
|
411
411
|
</div>
|
|
412
412
|
<div class="session-msg-content" v-html="highlightQueryText(msg.text) || ''"></div>
|
|
413
|
+
<button
|
|
414
|
+
v-if="msg.normalizedRole === 'user'"
|
|
415
|
+
type="button"
|
|
416
|
+
class="session-msg-copy"
|
|
417
|
+
@click="copySessionMessage(msg)"
|
|
418
|
+
:aria-label="t('sessions.preview.copyMessage')"
|
|
419
|
+
:title="t('sessions.preview.copyMessage')">
|
|
420
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
421
|
+
<rect x="8" y="8" width="12" height="12" rx="2"></rect>
|
|
422
|
+
<path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"></path>
|
|
423
|
+
</svg>
|
|
424
|
+
</button>
|
|
413
425
|
</div>
|
|
414
426
|
<div v-if="canLoadMoreSessionMessages" class="session-preview-load-more">
|
|
415
427
|
<span>{{ t('sessions.preview.shownCount', { shown: activeSessionVisibleMessages.length, total: activeSessionMessages.length }) }}</span>
|
|
@@ -138,6 +138,48 @@
|
|
|
138
138
|
</div>
|
|
139
139
|
</div>
|
|
140
140
|
</section>
|
|
141
|
+
|
|
142
|
+
<section class="settings-card" :aria-label="t('settings.configTabs.title')">
|
|
143
|
+
<div class="settings-card-main">
|
|
144
|
+
<div class="settings-card-content">
|
|
145
|
+
<div class="settings-card-title">{{ t('settings.configTabs.title') }}</div>
|
|
146
|
+
<p class="settings-card-desc">{{ t('settings.configTabs.meta') }}</p>
|
|
147
|
+
<div class="settings-toggle-grid">
|
|
148
|
+
<label class="settings-toggle-row">
|
|
149
|
+
<input type="checkbox" :checked="configModeVisibility.codex" @change="setConfigModeVisibility('codex', $event.target.checked)">
|
|
150
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
151
|
+
<span>{{ t('tab.config.codex') }}</span>
|
|
152
|
+
</label>
|
|
153
|
+
<label class="settings-toggle-row">
|
|
154
|
+
<input type="checkbox" :checked="configModeVisibility.claude" @change="setConfigModeVisibility('claude', $event.target.checked)">
|
|
155
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
156
|
+
<span>{{ t('tab.config.claude') }}</span>
|
|
157
|
+
</label>
|
|
158
|
+
<label class="settings-toggle-row">
|
|
159
|
+
<input type="checkbox" :checked="configModeVisibility.openclaw" @change="setConfigModeVisibility('openclaw', $event.target.checked)">
|
|
160
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
161
|
+
<span>{{ t('tab.config.openclaw') }}</span>
|
|
162
|
+
</label>
|
|
163
|
+
<label class="settings-toggle-row">
|
|
164
|
+
<input type="checkbox" :checked="configModeVisibility.opencode" @change="setConfigModeVisibility('opencode', $event.target.checked)">
|
|
165
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
166
|
+
<span>{{ t('tab.config.opencode') }}</span>
|
|
167
|
+
</label>
|
|
168
|
+
<label class="settings-toggle-row">
|
|
169
|
+
<input type="checkbox" :checked="configModeVisibility.kilocode" @change="setConfigModeVisibility('kilocode', $event.target.checked)">
|
|
170
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
171
|
+
<span>{{ t('tab.config.kilocode') }}</span>
|
|
172
|
+
</label>
|
|
173
|
+
<label class="settings-toggle-row">
|
|
174
|
+
<input type="checkbox" :checked="configModeVisibility.pi" @change="setConfigModeVisibility('pi', $event.target.checked)">
|
|
175
|
+
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
176
|
+
<span>{{ t('tab.config.pi') }}</span>
|
|
177
|
+
</label>
|
|
178
|
+
</div>
|
|
179
|
+
<p class="settings-card-hint">{{ t('settings.configTabs.hint') }}</p>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</section>
|
|
141
183
|
</div>
|
|
142
184
|
</div>
|
|
143
185
|
|