codexmate 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ <!-- Pi 新增供应商模板选择模态框 -->
2
+ <div v-if="showAddPiProviderModal && !editingPiProvider" class="modal-overlay" @click.self="cancelAddPiProviderModal">
3
+ <div class="modal modal-wide" role="dialog" aria-modal="true" aria-labelledby="pi-provider-picker-modal-title">
4
+ <div class="modal-title" id="pi-provider-picker-modal-title">{{ t('pi.providers.addModal.title') }}</div>
5
+ <div class="config-template-hint">{{ t('pi.providers.addModal.subtitle') }}</div>
6
+ <div class="pi-picker">
7
+ <input class="form-input" type="text" v-model="piProviderPickerQuery" autocomplete="off" spellcheck="false" :placeholder="t('pi.picker.searchPlaceholder')">
8
+ <div class="pi-picker-group" v-for="group in piProviderTemplateGroups()" :key="group.id">
9
+ <div class="pi-picker-group-title">{{ t(group.i18nKey) }}</div>
10
+ <div class="pi-picker-grid">
11
+ <button type="button" class="card pi-picker-card" v-for="tpl in group.templates" :key="tpl.id" @click="selectPiProviderTemplate(tpl.id)">
12
+ <span class="card-icon">{{ tpl.name.charAt(0).toUpperCase() }}</span>
13
+ <span class="pi-picker-card-name">{{ tpl.name }}</span>
14
+ <span v-if="tpl.baseUrl" class="pi-picker-card-url">{{ tpl.baseUrl }}</span>
15
+ </button>
16
+ </div>
17
+ </div>
18
+ <p v-if="piProviderPickerQuery.trim() && !piProviderTemplateGroups().length" class="config-template-hint">{{ t('pi.picker.empty') }}</p>
19
+ </div>
20
+ <div class="btn-group">
21
+ <button class="btn btn-cancel" @click="cancelAddPiProviderModal">{{ t('common.cancel') }}</button>
22
+ </div>
23
+ </div>
24
+ </div>
25
+
26
+ <!-- Pi 供应商编辑器模态框 -->
27
+ <div v-if="editingPiProvider" class="modal-overlay" @click.self="closePiProviderEditor">
28
+ <div class="modal modal-wide" role="dialog" aria-modal="true" aria-labelledby="pi-provider-editor-modal-title">
29
+ <div class="modal-title" id="pi-provider-editor-modal-title">{{ editingPiProvider.isNew ? t('pi.providers.addModal.title') : t('pi.providers.editorTitle') }}</div>
30
+ <div v-if="!editingPiProvider.isNew" class="form-group">
31
+ <label class="form-label">{{ t('pi.providers.fields.name') }}</label>
32
+ <input class="form-input" type="text" :value="piProviderName(editingPiProvider.id)" readonly>
33
+ </div>
34
+ <div class="form-group">
35
+ <label class="form-label">{{ t('pi.providers.fields.baseUrl') }}</label>
36
+ <input class="form-input" type="url" v-model="editingPiProvider.form.baseUrl" autocomplete="off" spellcheck="false" placeholder="https://" @change="fetchPiRemoteModels">
37
+ </div>
38
+ <div class="form-group">
39
+ <label class="form-label">{{ t('pi.providers.fields.api') }}</label>
40
+ <select class="form-input" v-model="editingPiProvider.form.api" :disabled="!isToolConfigWriteAllowed('pi')">
41
+ <option value="openai-completions">openai-completions — {{ t('pi.providers.addModal.api.openaiCompletions') }}</option>
42
+ <option value="openai-responses">openai-responses — {{ t('pi.providers.addModal.api.openaiResponses') }}</option>
43
+ <option value="anthropic-messages">anthropic-messages — {{ t('pi.providers.addModal.api.anthropicMessages') }}</option>
44
+ <option value="google-generative-ai">google-generative-ai — {{ t('pi.providers.addModal.api.googleGenerativeAi') }}</option>
45
+ <option v-if="!['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai'].includes(editingPiProvider.form.api)" :value="editingPiProvider.form.api">{{ editingPiProvider.form.api || '—' }}</option>
46
+ </select>
47
+ </div>
48
+ <div class="form-group">
49
+ <label class="form-label">{{ t('pi.providers.fields.apiKey') }}</label>
50
+ <div class="input-with-toggle">
51
+ <input class="form-input" v-model="editingPiProvider.form.apiKey" :type="piShowKey ? 'text' : 'password'" autocomplete="off" spellcheck="false" placeholder="sk-..." @change="fetchPiRemoteModels">
52
+ <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')">
53
+ <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>
54
+ <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>
55
+ </button>
56
+ </div>
57
+ </div>
58
+ <div class="form-group">
59
+ <label class="form-label">{{ t('pi.providers.fields.models') }}</label>
60
+ <p v-if="piRemoteModelsLoading" class="config-template-hint">{{ t('pi.providers.models.remoteLoading') }}</p>
61
+ <p v-else-if="piRemoteModelError" class="config-template-hint">{{ piRemoteModelError }} · {{ t('pi.providers.models.remoteFailed') }}</p>
62
+ <div class="card-list">
63
+ <div class="card" v-for="(model, index) in editingPiProvider.form.models" :key="index">
64
+ <div class="input-with-toggle">
65
+ <input class="form-input" type="text" v-model="model.id" autocomplete="off" spellcheck="false" placeholder="claude-sonnet-4" :disabled="!isToolConfigWriteAllowed('pi')">
66
+ <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>
67
+ </div>
68
+ </div>
69
+ <div class="card">
70
+ <div class="input-with-toggle">
71
+ <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())">
72
+ </div>
73
+ </div>
74
+ <div class="pi-model-import-section">
75
+ <span class="selector-title">{{ t('pi.providers.models.importTitle') }}</span>
76
+ <div class="pi-model-import-list" v-if="!piRemoteModelsLoading && piRemoteSelectableModels().length > 0">
77
+ <label class="pi-model-import-item" v-for="id in piRemoteSelectableModels()" :key="id">
78
+ <input type="checkbox" :checked="isPiRemoteChecked(id)" @change="togglePiRemoteChecked(id)" :disabled="!isToolConfigWriteAllowed('pi')">
79
+ <span class="pi-model-import-id">{{ id }}</span>
80
+ </label>
81
+ </div>
82
+ <div class="settings-tab-actions" v-if="!piRemoteModelsLoading && piRemoteSelectableModels().length > 0">
83
+ <button type="button" class="btn-tool" :disabled="!isToolConfigWriteAllowed('pi') || piCheckedRemoteCount() === 0" @click="addSelectedPiRemoteModels">{{ t('pi.providers.models.addSelected', { count: piCheckedRemoteCount() }) }}</button>
84
+ </div>
85
+ <p v-if="!piRemoteModelsLoading && !piRemoteModelError && piRemoteModels.length" class="config-template-hint">{{ t('pi.providers.models.remoteCount', { count: piRemoteModels.length }) }}</p>
86
+ <p v-if="!piRemoteModelsLoading && !piRemoteModelError && piModelSearch.trim() && piRemoteSelectableModels().length === 0" class="config-template-hint">{{ t('pi.providers.models.noMatch') }}</p>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ <div class="btn-group">
91
+ <button class="btn btn-cancel" @click="closePiProviderEditor">{{ t('common.cancel') }}</button>
92
+ <button v-if="!editingPiProvider.isNew" class="btn btn-confirm btn-danger" @click="confirmDeletePiProvider" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('pi.providers.delete') }}</button>
93
+ <button class="btn btn-confirm" @click="savePiProvider" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t(piSaving ? 'common.saving' : 'common.save') }}</button>
94
+ </div>
95
+ </div>
96
+ </div>
@@ -56,128 +56,6 @@
56
56
  <div v-if="!piProviderIds.length" class="config-template-hint">{{ t('pi.providers.empty') }}</div>
57
57
  </section>
58
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
-
121
- <section class="selector-section">
122
- <div class="selector-header">
123
- <span class="selector-title">{{ t('pi.providers.json.title') }}</span>
124
- </div>
125
- <p class="config-template-hint">{{ t('pi.providers.json.desc') }}</p>
126
- <label class="form-label">{{ t('pi.providers.json.previewTitle') }}</label>
127
- <pre class="config-json-preview">{{ piProviderPreviewJson() }}</pre>
128
- <label class="form-label">{{ t('pi.providers.json.editTitle') }}</label>
129
- <textarea
130
- class="template-textarea config-json-editor"
131
- v-model="editingPiProvider.form.configJsonDraft"
132
- spellcheck="false"
133
- autocomplete="off"
134
- :disabled="!isToolConfigWriteAllowed('pi')"
135
- @input="piEditorJsonDraftInput"
136
- placeholder="{}"></textarea>
137
- <p v-if="piEditorJsonError" class="config-template-hint error-text">{{ piEditorJsonError }}</p>
138
- </section>
139
- </section>
140
-
141
- <section class="selector-section" v-if="showAddPiProviderModal">
142
- <div class="selector-header">
143
- <span class="selector-title">{{ t('pi.providers.addModal.title') }}</span>
144
- <div class="settings-tab-actions">
145
- <button type="button" class="btn-tool" @click="addPiProviderFromModal" :disabled="!isToolConfigWriteAllowed('pi') || piSaving">{{ t('common.save') }}</button>
146
- <button type="button" class="btn-tool" @click="cancelAddPiProviderModal">{{ t('common.cancel') }}</button>
147
- </div>
148
- </div>
149
- <div class="config-template-hint">{{ t('pi.providers.addModal.subtitle') }}</div>
150
- <div class="codex-config-grid">
151
- <div class="form-group codex-config-field">
152
- <label class="form-label">{{ t('pi.providers.addModal.fields.baseUrl') }}</label>
153
- <input class="form-input" type="url" v-model="addingPiProviderBaseUrl" autocomplete="off" spellcheck="false" placeholder="https://" @input="onAddingPiEndpointChange">
154
- </div>
155
- <div class="form-group codex-config-field">
156
- <label class="form-label">{{ t('pi.providers.addModal.fields.api') }}</label>
157
- <select class="form-input" v-model="addingPiProviderApi">
158
- <option value="openai-completions">openai-completions — {{ t('pi.providers.addModal.api.openaiCompletions') }}</option>
159
- <option value="openai-responses">openai-responses — {{ t('pi.providers.addModal.api.openaiResponses') }}</option>
160
- <option value="anthropic-messages">anthropic-messages — {{ t('pi.providers.addModal.api.anthropicMessages') }}</option>
161
- <option value="google-generative-ai">google-generative-ai — {{ t('pi.providers.addModal.api.googleGenerativeAi') }}</option>
162
- </select>
163
- </div>
164
- <div class="form-group codex-config-field">
165
- <label class="form-label">{{ t('pi.providers.addModal.fields.apiKey') }}</label>
166
- <input class="form-input" type="password" v-model="addingPiProviderApiKey" autocomplete="off" spellcheck="false" :placeholder="t('common.optional')" @input="onAddingPiEndpointChange">
167
- </div>
168
- <div class="form-group codex-config-field">
169
- <label class="form-label">{{ t('pi.providers.fields.models') }}</label>
170
- <p v-if="addingPiRemoteLoading" class="config-template-hint">{{ t('pi.providers.models.remoteLoading') }}</p>
171
- <p v-else-if="addingPiRemoteError" class="config-template-hint">{{ addingPiRemoteError }} · {{ t('pi.providers.models.remoteFailed') }}</p>
172
- <input class="form-input" type="text" v-model="addingPiProviderModel" autocomplete="off" spellcheck="false" :placeholder="addingPiRemoteModels.length ? t('pi.providers.models.search') : 'claude-sonnet-4'">
173
- <template v-if="!addingPiRemoteLoading && addingPiProviderModel.trim() && piFilteredAddingRemoteModels().length > 0">
174
- <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>
175
- <p class="config-template-hint">{{ t('pi.providers.models.remoteCount', { count: addingPiRemoteModels.length }) }}</p>
176
- </template>
177
- <p v-else-if="!addingPiRemoteLoading && !addingPiRemoteError && addingPiProviderModel.trim() && addingPiRemoteModels.length > 0" class="config-template-hint">{{ t('pi.providers.models.noMatch') }}</p>
178
- </div>
179
- </div>
180
- </section>
181
59
  <section class="selector-section">
182
60
  <div class="selector-header">
183
61
  <span class="selector-title">{{ t('pi.files.title') }}</span>