codexmate 0.1.12 → 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 -3
- package/README.vi.md +4 -3
- package/README.zh.md +5 -3
- package/cli.js +139 -4
- package/package.json +1 -1
- package/web-ui/app.js +81 -5
- package/web-ui/index.html +2 -1
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.navigation.mjs +1 -1
- package/web-ui/modules/app.methods.pi-config.mjs +371 -0
- package/web-ui/modules/app.methods.session-actions.mjs +4 -4
- package/web-ui/modules/app.methods.startup-claude.mjs +2 -1
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +3 -2
- package/web-ui/modules/app.methods.web-ui-preferences.mjs +2 -1
- package/web-ui/modules/config-mode.computed.mjs +2 -1
- package/web-ui/modules/i18n/locales/en.mjs +43 -0
- package/web-ui/modules/i18n/locales/ja.mjs +43 -0
- package/web-ui/modules/i18n/locales/vi.mjs +43 -0
- package/web-ui/modules/i18n/locales/zh-tw.mjs +43 -0
- package/web-ui/modules/i18n/locales/zh.mjs +43 -0
- package/web-ui/partials/index/layout-header.html +15 -0
- package/web-ui/partials/index/panel-config-pi.html +171 -0
- package/web-ui/partials/index/panel-settings.html +7 -0
- package/web-ui/res/web-ui-render.precompiled.js +567 -49
- package/web-ui/styles/settings-panel.css +7 -0
|
@@ -13,6 +13,7 @@ const zhTw = Object.freeze({
|
|
|
13
13
|
'common.paste': '粘貼',
|
|
14
14
|
'common.edit': '編輯',
|
|
15
15
|
'common.install': '安裝',
|
|
16
|
+
'common.optional': '選填',
|
|
16
17
|
'common.update': '升級',
|
|
17
18
|
'common.uninstall': '解除安裝',
|
|
18
19
|
'common.official': '官方',
|
|
@@ -135,6 +136,7 @@ const zhTw = Object.freeze({
|
|
|
135
136
|
'tab.config.claude': 'Claude',
|
|
136
137
|
'tab.config.openclaw': 'OpenClaw',
|
|
137
138
|
'tab.config.opencode': 'OpenCode',
|
|
139
|
+
'tab.config.pi': 'Pi',
|
|
138
140
|
'tab.sessions': '會話',
|
|
139
141
|
'tab.usage': '用量',
|
|
140
142
|
'tab.market': 'Skills',
|
|
@@ -1317,6 +1319,47 @@ const zhTw = Object.freeze({
|
|
|
1317
1319
|
'toolConfig.openclaw.confirmMessage': '開啟後,OpenClaw tab 內的操作會寫入 openclaw.json 及 OpenClaw 工作區檔案(AGENTS.md、SOUL.md 等)。',
|
|
1318
1320
|
'toolConfig.openclaw.lockedTitle': 'OpenClaw 目前為唯讀',
|
|
1319
1321
|
'toolConfig.openclaw.lockedDesc': '這裡不會寫入 OpenClaw 設定。要套用設定或編輯工作區檔案,請先開啟本 tab 的寫入開關。',
|
|
1322
|
+
'side.config.pi': 'Pi',
|
|
1323
|
+
'side.config.pi.meta': '設定',
|
|
1324
|
+
'toolConfig.pi.title': 'Pi 設定寫入',
|
|
1325
|
+
'toolConfig.pi.desc': '預設唯讀;開啟後可同步 Pi 設定。',
|
|
1326
|
+
'toolConfig.pi.lockedTitle': '唯讀模式',
|
|
1327
|
+
'toolConfig.pi.lockedDesc': '開啟寫入權限後可同步 Pi 設定。',
|
|
1328
|
+
'toolConfig.pi.confirmMessage': '開啟後,Pi tab 內的操作會寫入 ~/.pi/config.json 或專案內的 ./.pi/config.json。',
|
|
1329
|
+
'pi.status.readOnly': '唯讀',
|
|
1330
|
+
'pi.config.openGlobal': '打開全域設定',
|
|
1331
|
+
'pi.providers.title': 'Pi 供應商',
|
|
1332
|
+
'pi.providers.subtitle': '管理 Piper 本地供應商與模型',
|
|
1333
|
+
'pi.providers.empty': '未找到 Piper 供應商,點擊新增開始。',
|
|
1334
|
+
'pi.providers.targetFile': 'Pi 生效設定檔:{path}',
|
|
1335
|
+
'pi.providers.editorTitle': '編輯供應商',
|
|
1336
|
+
'pi.providers.add': '新增供應商',
|
|
1337
|
+
'pi.providers.active': '已啟用',
|
|
1338
|
+
'pi.providers.setActive': '設為使用中',
|
|
1339
|
+
'pi.providers.deleteTitle': '刪除 Pi 設定',
|
|
1340
|
+
'pi.providers.deleteMessage': '確定刪除供應商 "{name}"?',
|
|
1341
|
+
'pi.providers.fields.name': '名稱',
|
|
1342
|
+
'pi.providers.fields.baseUrl': 'Base URL',
|
|
1343
|
+
'pi.providers.fields.api': '介面格式',
|
|
1344
|
+
'pi.providers.fields.apiKey': 'API Key',
|
|
1345
|
+
'pi.providers.fields.models': '模型',
|
|
1346
|
+
'pi.providers.fields.configJson': '組態 JSON',
|
|
1347
|
+
'pi.providers.models.remove': '刪除',
|
|
1348
|
+
'pi.providers.models.search': '搜尋可用模型…',
|
|
1349
|
+
'pi.providers.models.remoteCount': '已取得 {count} 個可用模型,點擊新增',
|
|
1350
|
+
'pi.providers.models.remoteLoading': '正在取得遠端模型清單…',
|
|
1351
|
+
'pi.providers.models.remoteFailed': '可手動輸入模型 ID。',
|
|
1352
|
+
'pi.providers.models.noMatch': '無符合模型',
|
|
1353
|
+
'pi.providers.delete': '刪除供應商',
|
|
1354
|
+
'pi.providers.addModal.title': '新增供應商',
|
|
1355
|
+
'pi.providers.addModal.subtitle': '建立新的 Piper 供應商',
|
|
1356
|
+
'pi.providers.addModal.fields.baseUrl': 'Base URL',
|
|
1357
|
+
'pi.providers.addModal.fields.apiKey': 'API Key',
|
|
1358
|
+
'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions(最相容)',
|
|
1359
|
+
'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
|
|
1360
|
+
'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
|
|
1361
|
+
'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
|
|
1362
|
+
'pi.providers.addModal.fields.api': '介面格式',
|
|
1320
1363
|
'kilocode.providerModel.title': 'KiloCode provider / 模型',
|
|
1321
1364
|
'kilocode.connection.title': 'KiloCode 連線',
|
|
1322
1365
|
'kilocode.targetFile': 'KiloCode 生效設定:{path} · {status}',
|
|
@@ -13,6 +13,7 @@ const zh = Object.freeze({
|
|
|
13
13
|
'common.paste': '粘贴',
|
|
14
14
|
'common.edit': '编辑',
|
|
15
15
|
'common.install': '安装',
|
|
16
|
+
'common.optional': '选填',
|
|
16
17
|
'common.update': '升级',
|
|
17
18
|
'common.uninstall': '卸载',
|
|
18
19
|
'common.official': '官方',
|
|
@@ -135,6 +136,7 @@ const zh = Object.freeze({
|
|
|
135
136
|
'tab.config.claude': 'Claude',
|
|
136
137
|
'tab.config.openclaw': 'OpenClaw',
|
|
137
138
|
'tab.config.opencode': 'OpenCode',
|
|
139
|
+
'tab.config.pi': 'Pi',
|
|
138
140
|
'tab.sessions': '会话',
|
|
139
141
|
'tab.usage': '用量',
|
|
140
142
|
'tab.market': 'Skills',
|
|
@@ -1317,6 +1319,47 @@ const zh = Object.freeze({
|
|
|
1317
1319
|
'toolConfig.openclaw.confirmMessage': '打开后,OpenClaw tab 内的操作会写入 openclaw.json 及 OpenClaw 工作区文件(AGENTS.md、SOUL.md 等)。',
|
|
1318
1320
|
'toolConfig.openclaw.lockedTitle': 'OpenClaw 当前为只读',
|
|
1319
1321
|
'toolConfig.openclaw.lockedDesc': '这里不会写入 OpenClaw 配置。要应用配置或编辑工作区文件,请先打开本 tab 的写入开关。',
|
|
1322
|
+
'side.config.pi': 'Pi',
|
|
1323
|
+
'side.config.pi.meta': '配置',
|
|
1324
|
+
'toolConfig.pi.title': 'Pi 配置写入',
|
|
1325
|
+
'toolConfig.pi.desc': '默认只读;开启后可同步 Pi 配置。',
|
|
1326
|
+
'toolConfig.pi.lockedTitle': '只读模式',
|
|
1327
|
+
'toolConfig.pi.lockedDesc': '开启写入权限后可同步 Pi 配置。',
|
|
1328
|
+
'toolConfig.pi.confirmMessage': '打开后,Pi tab 内的操作会写入 ~/.pi/config.json 或项目内的 ./.pi/config.json。',
|
|
1329
|
+
'pi.status.readOnly': '只读',
|
|
1330
|
+
'pi.config.openGlobal': '打开全局配置',
|
|
1331
|
+
'pi.providers.title': 'Pi 供应商',
|
|
1332
|
+
'pi.providers.subtitle': '管理 Piper 本地供应商与模型',
|
|
1333
|
+
'pi.providers.empty': '未找到 Piper 供应商,点击添加开始。',
|
|
1334
|
+
'pi.providers.targetFile': 'Pi 生效配置文件:{path}',
|
|
1335
|
+
'pi.providers.editorTitle': '编辑供应商',
|
|
1336
|
+
'pi.providers.add': '添加供应商',
|
|
1337
|
+
'pi.providers.active': '已启用',
|
|
1338
|
+
'pi.providers.setActive': '设为使用中',
|
|
1339
|
+
'pi.providers.deleteTitle': '删除 Pi 配置',
|
|
1340
|
+
'pi.providers.deleteMessage': '确定删除供应商 "{name}"?',
|
|
1341
|
+
'pi.providers.fields.name': '名称',
|
|
1342
|
+
'pi.providers.fields.baseUrl': 'Base URL',
|
|
1343
|
+
'pi.providers.fields.api': '接口格式',
|
|
1344
|
+
'pi.providers.fields.apiKey': 'API Key',
|
|
1345
|
+
'pi.providers.fields.models': '模型',
|
|
1346
|
+
'pi.providers.fields.configJson': '配置 JSON',
|
|
1347
|
+
'pi.providers.models.remove': '删除',
|
|
1348
|
+
'pi.providers.models.search': '搜索可用模型…',
|
|
1349
|
+
'pi.providers.models.remoteCount': '已拉取 {count} 个可用模型,点击添加',
|
|
1350
|
+
'pi.providers.models.remoteLoading': '正在获取远端模型列表…',
|
|
1351
|
+
'pi.providers.models.remoteFailed': '可手动输入模型 ID。',
|
|
1352
|
+
'pi.providers.models.noMatch': '无匹配模型',
|
|
1353
|
+
'pi.providers.delete': '删除供应商',
|
|
1354
|
+
'pi.providers.addModal.title': '添加供应商',
|
|
1355
|
+
'pi.providers.addModal.subtitle': '创建新的 Piper 供应商',
|
|
1356
|
+
'pi.providers.addModal.fields.baseUrl': 'Base URL',
|
|
1357
|
+
'pi.providers.addModal.fields.apiKey': 'API Key',
|
|
1358
|
+
'pi.providers.addModal.api.openaiCompletions': 'OpenAI Chat Completions(最兼容)',
|
|
1359
|
+
'pi.providers.addModal.api.openaiResponses': 'OpenAI Responses API',
|
|
1360
|
+
'pi.providers.addModal.api.anthropicMessages': 'Anthropic Messages API',
|
|
1361
|
+
'pi.providers.addModal.api.googleGenerativeAi': 'Google Generative AI',
|
|
1362
|
+
'pi.providers.addModal.fields.api': '接口格式',
|
|
1320
1363
|
'kilocode.providerModel.title': 'KiloCode provider / model',
|
|
1321
1364
|
'kilocode.connection.title': 'KiloCode 连接',
|
|
1322
1365
|
'kilocode.targetFile': 'KiloCode 生效配置:{path} · {status}',
|
|
@@ -258,6 +258,21 @@
|
|
|
258
258
|
<span>{{ t('side.config.kilocode.meta') }}</span>
|
|
259
259
|
</div>
|
|
260
260
|
</button>
|
|
261
|
+
<button
|
|
262
|
+
v-if="isConfigModeVisible('pi')"
|
|
263
|
+
id="side-tab-config-pi"
|
|
264
|
+
data-main-tab="config"
|
|
265
|
+
data-config-mode="pi"
|
|
266
|
+
:aria-current="mainTab === 'config' && configMode === 'pi' ? 'page' : null"
|
|
267
|
+
:class="['side-item', 'side-item-compact', { active: isConfigModeNavActive('pi') }]"
|
|
268
|
+
@pointerdown="onConfigTabPointerDown('pi', $event)"
|
|
269
|
+
@click="onConfigTabClick('pi', $event)">
|
|
270
|
+
<span class="side-item-icon" aria-hidden="true">P</span>
|
|
271
|
+
<div class="side-item-title">{{ t('side.config.pi') }}</div>
|
|
272
|
+
<div class="side-item-meta">
|
|
273
|
+
<span>{{ t('side.config.pi.meta') }}</span>
|
|
274
|
+
</div>
|
|
275
|
+
</button>
|
|
261
276
|
</div>
|
|
262
277
|
|
|
263
278
|
|
|
@@ -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>
|
|
@@ -144,6 +144,7 @@
|
|
|
144
144
|
<div class="settings-card-content">
|
|
145
145
|
<div class="settings-card-title">{{ t('settings.configTabs.title') }}</div>
|
|
146
146
|
<p class="settings-card-desc">{{ t('settings.configTabs.meta') }}</p>
|
|
147
|
+
<div class="settings-toggle-grid">
|
|
147
148
|
<label class="settings-toggle-row">
|
|
148
149
|
<input type="checkbox" :checked="configModeVisibility.codex" @change="setConfigModeVisibility('codex', $event.target.checked)">
|
|
149
150
|
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
@@ -169,6 +170,12 @@
|
|
|
169
170
|
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
|
170
171
|
<span>{{ t('tab.config.kilocode') }}</span>
|
|
171
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>
|
|
172
179
|
<p class="settings-card-hint">{{ t('settings.configTabs.hint') }}</p>
|
|
173
180
|
</div>
|
|
174
181
|
</div>
|