codexmate 0.0.31 → 0.0.33
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 +92 -308
- package/README.zh.md +94 -318
- package/cli/local-bridge.js +227 -0
- package/cli/update.js +162 -0
- package/cli.js +357 -112
- package/lib/cli-sessions.js +16 -6
- package/lib/win-tray.js +119 -0
- package/package.json +2 -2
- package/web-ui/app.js +4 -0
- package/web-ui/logic.sessions.mjs +17 -1
- package/web-ui/modules/app.computed.session.mjs +51 -315
- package/web-ui/modules/app.methods.agents.mjs +19 -0
- package/web-ui/modules/app.methods.claude-config.mjs +71 -2
- package/web-ui/modules/app.methods.codex-config.mjs +20 -0
- package/web-ui/modules/app.methods.providers.mjs +53 -7
- package/web-ui/modules/app.methods.session-actions.mjs +1 -1
- package/web-ui/modules/app.methods.session-browser.mjs +29 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +4 -0
- package/web-ui/modules/i18n.dict.mjs +21 -3
- package/web-ui/partials/index/layout-header.html +1 -2
- package/web-ui/partials/index/modal-config-template-agents.html +12 -1
- package/web-ui/partials/index/modals-basic.html +14 -3
- package/web-ui/partials/index/panel-config-claude.html +57 -85
- package/web-ui/partials/index/panel-config-codex.html +60 -226
- package/web-ui/partials/index/panel-dashboard.html +0 -33
- package/web-ui/partials/index/panel-docs.html +21 -53
- package/web-ui/partials/index/panel-sessions.html +37 -20
- package/web-ui/partials/index/panel-trash.html +33 -38
- package/web-ui/partials/index/panel-usage.html +71 -304
- package/web-ui/styles/controls-forms.css +11 -0
- package/web-ui/styles/docs-panel.css +57 -83
- package/web-ui/styles/layout-shell.css +26 -24
- package/web-ui/styles/modals-core.css +33 -0
- package/web-ui/styles/responsive.css +5 -67
- package/web-ui/styles/sessions-list.css +274 -8
- package/web-ui/styles/sessions-toolbar-trash.css +185 -15
- package/web-ui/styles/sessions-usage.css +336 -788
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!--
|
|
1
|
+
<!-- Codex 配置 -->
|
|
2
2
|
<div
|
|
3
3
|
v-show="mainTab === 'config' && isProviderConfigMode"
|
|
4
4
|
class="mode-content mode-cards"
|
|
@@ -18,11 +18,7 @@
|
|
|
18
18
|
<div class="docs-command-row">
|
|
19
19
|
<div class="docs-command-box" role="group" :aria-label="t('cli.missing.commandAria', { name: 'Codex' })">
|
|
20
20
|
<code class="install-command">{{ getInstallCommand('codex', 'install') }}</code>
|
|
21
|
-
<button
|
|
22
|
-
type="button"
|
|
23
|
-
class="btn-mini docs-copy-btn"
|
|
24
|
-
:disabled="!getInstallCommand('codex', 'install')"
|
|
25
|
-
@click="copyInstallCommand(getInstallCommand('codex', 'install'))">{{ t('common.copy') }}</button>
|
|
21
|
+
<button type="button" class="btn-mini docs-copy-btn" :disabled="!getInstallCommand('codex', 'install')" @click="copyInstallCommand(getInstallCommand('codex', 'install'))">{{ t('common.copy') }}</button>
|
|
26
22
|
</div>
|
|
27
23
|
</div>
|
|
28
24
|
<button type="button" class="btn-tool btn-tool-compact" @click="mainTab = 'docs'; setInstallCommandAction('install')">{{ t('cli.missing.openDocs') }}</button>
|
|
@@ -30,36 +26,25 @@
|
|
|
30
26
|
</div>
|
|
31
27
|
</template>
|
|
32
28
|
<template v-else>
|
|
33
|
-
<!-- 添加提供商按钮 -->
|
|
34
29
|
<button class="btn-add" @click="showAddModal = true" v-if="!loading && !initError">
|
|
35
|
-
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
36
|
-
<path d="M10 4v12M4 10h12"/>
|
|
37
|
-
</svg>
|
|
30
|
+
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 4v12M4 10h12"/></svg>
|
|
38
31
|
{{ t('config.addProvider') }}
|
|
39
32
|
</button>
|
|
40
33
|
|
|
41
|
-
<!-- 服务预设 -->
|
|
42
34
|
<div class="selector-section" v-if="isCodexConfigMode && codexProviderTemplates.length">
|
|
43
35
|
<div class="selector-header">
|
|
44
36
|
<span class="selector-title">{{ t('config.providerTemplate.title') }}</span>
|
|
45
37
|
</div>
|
|
46
38
|
<div class="btn-group" style="flex-wrap: wrap; gap: 8px; margin-top: 0;">
|
|
47
|
-
<button
|
|
48
|
-
v-for="tpl in codexProviderTemplates"
|
|
49
|
-
:key="tpl.name"
|
|
50
|
-
type="button"
|
|
51
|
-
class="btn-mini"
|
|
52
|
-
@click="newProvider.name = tpl.name;
|
|
53
|
-
newProvider.url = tpl.url;
|
|
54
|
-
newProvider._suggestedModel = tpl.model || '';
|
|
55
|
-
newProvider.useTransform = !!tpl.useTransform;
|
|
56
|
-
showAddModal = true">
|
|
57
|
-
{{ tpl.label }}
|
|
58
|
-
</button>
|
|
39
|
+
<button v-for="tpl in codexProviderTemplates" :key="tpl.name" type="button" class="btn-mini" @click="newProvider.name = tpl.name; newProvider.url = tpl.url; newProvider._suggestedModel = tpl.model || ''; newProvider.useTransform = !!tpl.useTransform; showAddModal = true">{{ tpl.label }}</button>
|
|
59
40
|
</div>
|
|
60
41
|
</div>
|
|
61
42
|
|
|
62
|
-
|
|
43
|
+
<div class="selector-section">
|
|
44
|
+
<div class="selector-header"><span class="selector-title">AGENTS.md</span></div>
|
|
45
|
+
<button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">{{ agentsLoading ? t('config.modelLoading') : t('config.agents.open') }}</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
63
48
|
<div class="selector-section">
|
|
64
49
|
<div class="selector-header">
|
|
65
50
|
<span class="selector-title">{{ t('config.models') }}</span>
|
|
@@ -68,74 +53,41 @@
|
|
|
68
53
|
<button class="btn-icon" @click="showModelListModal = true" :aria-label="t('modal.modelManage.title')" :title="t('modal.modelManage.title')" v-if="modelsSource === 'legacy'">≡</button>
|
|
69
54
|
</div>
|
|
70
55
|
</div>
|
|
71
|
-
<select
|
|
72
|
-
v-if="codexModelsLoading || modelsSource === 'remote'"
|
|
73
|
-
class="model-select"
|
|
74
|
-
v-model="currentModel"
|
|
75
|
-
@change="onModelChange"
|
|
76
|
-
:disabled="codexModelsLoading"
|
|
77
|
-
>
|
|
56
|
+
<select v-if="codexModelsLoading || modelsSource === 'remote'" class="model-select" v-model="currentModel" @change="onModelChange" :disabled="codexModelsLoading">
|
|
78
57
|
<option v-if="codexModelsLoading" value="">{{ t('config.modelLoading') }}</option>
|
|
79
58
|
<option v-else v-for="model in codexModelOptions" :key="model" :value="model">{{ model }}</option>
|
|
80
59
|
</select>
|
|
81
|
-
<input
|
|
82
|
-
v-if="!codexModelsLoading && (modelsSource !== 'remote' || !modelsHasCurrent)"
|
|
83
|
-
class="model-input"
|
|
84
|
-
v-model="currentModel"
|
|
85
|
-
@blur="onModelChange"
|
|
86
|
-
@keyup.enter="onModelChange"
|
|
87
|
-
:placeholder="activeProviderModelPlaceholder"
|
|
88
|
-
:list="codexModelHasList ? 'codex-model-options' : null"
|
|
89
|
-
>
|
|
60
|
+
<input v-if="!codexModelsLoading && (modelsSource !== 'remote' || !modelsHasCurrent)" class="model-input" v-model="currentModel" @blur="onModelChange" @keyup.enter="onModelChange" :placeholder="activeProviderModelPlaceholder" :list="codexModelHasList ? 'codex-model-options' : null">
|
|
90
61
|
<datalist v-if="codexModelHasList" id="codex-model-options">
|
|
91
62
|
<option v-for="model in codexModelOptions" :key="model" :value="model"></option>
|
|
92
63
|
</datalist>
|
|
93
|
-
<div class="config-template-hint" v-if="modelsSource === 'unlimited'">
|
|
94
|
-
|
|
95
|
-
</div>
|
|
96
|
-
<div class="config-template-hint" v-if="
|
|
97
|
-
|
|
98
|
-
</
|
|
99
|
-
<div class="config-template-hint" v-if="modelsSource === 'remote' && !modelsHasCurrent">
|
|
100
|
-
{{ isCodexConfigMode ? t('config.models.notInList.codex') : t('config.models.notInList.other') }}
|
|
101
|
-
</div>
|
|
102
|
-
<div class="config-template-hint" v-if="isCodexConfigMode">
|
|
103
|
-
{{ t('config.template.editFirst') }}
|
|
104
|
-
</div>
|
|
105
|
-
<div class="config-template-hint" v-else-if="activeProviderBridgeHint">
|
|
106
|
-
{{ t('config.template.bridgeCodexOnly', { hint: activeProviderBridgeHint }) }}
|
|
107
|
-
</div>
|
|
108
|
-
<button class="btn-tool btn-template-editor" v-if="isCodexConfigMode" @click="openConfigTemplateEditor" :disabled="loading || !!initError">
|
|
109
|
-
{{ t('config.template.openEditor') }}
|
|
110
|
-
</button>
|
|
64
|
+
<div class="config-template-hint" v-if="modelsSource === 'unlimited'">{{ t('config.models.unlimited') }}</div>
|
|
65
|
+
<div class="config-template-hint" v-if="modelsSource === 'error'">{{ t('config.models.error') }}</div>
|
|
66
|
+
<div class="config-template-hint" v-if="modelsSource === 'remote' && !modelsHasCurrent">{{ isCodexConfigMode ? t('config.models.notInList.codex') : t('config.models.notInList.other') }}</div>
|
|
67
|
+
<div class="config-template-hint" v-if="isCodexConfigMode">{{ t('config.template.editFirst') }}</div>
|
|
68
|
+
<div class="config-template-hint" v-else-if="activeProviderBridgeHint">{{ t('config.template.bridgeCodexOnly', { hint: activeProviderBridgeHint }) }}</div>
|
|
69
|
+
<button class="btn-tool btn-template-editor" v-if="isCodexConfigMode" @click="openConfigTemplateEditor" :disabled="loading || !!initError">{{ t('config.template.openEditor') }}</button>
|
|
111
70
|
</div>
|
|
112
71
|
|
|
113
72
|
<template v-if="isCodexConfigMode">
|
|
114
|
-
<div class="
|
|
115
|
-
<div class="selector-
|
|
116
|
-
<span class="selector-title">{{ t('config.serviceTier') }}</span>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<option value="high">high</option>
|
|
133
|
-
<option value="medium">{{ t('config.reasoningEffort.medium') }}</option>
|
|
134
|
-
<option value="low">low</option>
|
|
135
|
-
<option value="xhigh">xhigh</option>
|
|
136
|
-
</select>
|
|
137
|
-
<div class="config-template-hint">
|
|
138
|
-
{{ t('config.reasoningEffort.hint') }}
|
|
73
|
+
<div class="config-row">
|
|
74
|
+
<div class="selector-section" style="flex:1">
|
|
75
|
+
<div class="selector-header"><span class="selector-title">{{ t('config.serviceTier') }}</span></div>
|
|
76
|
+
<select class="model-select" v-model="serviceTier" @change="onServiceTierChange">
|
|
77
|
+
<option value="fast">{{ t('config.serviceTier.fast') }}</option>
|
|
78
|
+
<option value="standard">{{ t('config.serviceTier.standard') }}</option>
|
|
79
|
+
</select>
|
|
80
|
+
<div class="config-template-hint">{{ t('config.serviceTier.hint', { field: 'service_tier' }) }}</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="selector-section" style="flex:1">
|
|
83
|
+
<div class="selector-header"><span class="selector-title">{{ t('config.reasoningEffort') }}</span></div>
|
|
84
|
+
<select class="model-select" v-model="modelReasoningEffort" @change="onReasoningEffortChange">
|
|
85
|
+
<option value="high">high</option>
|
|
86
|
+
<option value="medium">{{ t('config.reasoningEffort.medium') }}</option>
|
|
87
|
+
<option value="low">low</option>
|
|
88
|
+
<option value="xhigh">xhigh</option>
|
|
89
|
+
</select>
|
|
90
|
+
<div class="config-template-hint">{{ t('config.reasoningEffort.hint') }}</div>
|
|
139
91
|
</div>
|
|
140
92
|
</div>
|
|
141
93
|
|
|
@@ -143,92 +95,32 @@
|
|
|
143
95
|
<div class="selector-header">
|
|
144
96
|
<span class="selector-title">{{ t('config.contextBudget') }}</span>
|
|
145
97
|
<div class="selector-actions">
|
|
146
|
-
<button
|
|
147
|
-
class="btn-tool btn-tool-compact"
|
|
148
|
-
@click="resetCodexContextBudgetDefaults"
|
|
149
|
-
:disabled="loading || !!initError || codexApplying">
|
|
150
|
-
{{ t('config.reset') }}
|
|
151
|
-
</button>
|
|
98
|
+
<button class="btn-tool btn-tool-compact" @click="resetCodexContextBudgetDefaults" :disabled="loading || !!initError || codexApplying">{{ t('config.reset') }}</button>
|
|
152
99
|
</div>
|
|
153
100
|
</div>
|
|
154
101
|
<div class="codex-config-grid">
|
|
155
102
|
<div class="form-group codex-config-field">
|
|
156
103
|
<label class="form-label" for="codex-model-context-window">model_context_window</label>
|
|
157
|
-
<input
|
|
158
|
-
id="codex-model-context-window"
|
|
159
|
-
v-model="modelContextWindowInput"
|
|
160
|
-
class="form-input"
|
|
161
|
-
inputmode="numeric"
|
|
162
|
-
autocomplete="off"
|
|
163
|
-
:placeholder="t('config.example', { value: 190000 })"
|
|
164
|
-
@focus="editingCodexBudgetField = 'modelContextWindowInput'"
|
|
165
|
-
@input="sanitizePositiveIntegerDraft('modelContextWindowInput')"
|
|
166
|
-
@blur="onModelContextWindowBlur"
|
|
167
|
-
@keydown.enter.prevent="onModelContextWindowBlur">
|
|
104
|
+
<input id="codex-model-context-window" v-model="modelContextWindowInput" class="form-input" inputmode="numeric" autocomplete="off" :placeholder="t('config.example', { value: 190000 })" @focus="editingCodexBudgetField = 'modelContextWindowInput'" @input="sanitizePositiveIntegerDraft('modelContextWindowInput')" @blur="onModelContextWindowBlur" @keydown.enter.prevent="onModelContextWindowBlur">
|
|
168
105
|
<div class="form-hint">{{ t('config.contextWindow.hint') }}</div>
|
|
169
106
|
</div>
|
|
170
107
|
<div class="form-group codex-config-field">
|
|
171
108
|
<label class="form-label" for="codex-model-auto-compact-token-limit">model_auto_compact_token_limit</label>
|
|
172
|
-
<input
|
|
173
|
-
id="codex-model-auto-compact-token-limit"
|
|
174
|
-
v-model="modelAutoCompactTokenLimitInput"
|
|
175
|
-
class="form-input"
|
|
176
|
-
inputmode="numeric"
|
|
177
|
-
autocomplete="off"
|
|
178
|
-
:placeholder="t('config.example', { value: 185000 })"
|
|
179
|
-
@focus="editingCodexBudgetField = 'modelAutoCompactTokenLimitInput'"
|
|
180
|
-
@input="sanitizePositiveIntegerDraft('modelAutoCompactTokenLimitInput')"
|
|
181
|
-
@blur="onModelAutoCompactTokenLimitBlur"
|
|
182
|
-
@keydown.enter.prevent="onModelAutoCompactTokenLimitBlur">
|
|
109
|
+
<input id="codex-model-auto-compact-token-limit" v-model="modelAutoCompactTokenLimitInput" class="form-input" inputmode="numeric" autocomplete="off" :placeholder="t('config.example', { value: 185000 })" @focus="editingCodexBudgetField = 'modelAutoCompactTokenLimitInput'" @input="sanitizePositiveIntegerDraft('modelAutoCompactTokenLimitInput')" @blur="onModelAutoCompactTokenLimitBlur" @keydown.enter.prevent="onModelAutoCompactTokenLimitBlur">
|
|
183
110
|
<div class="form-hint">{{ t('config.autoCompact.hint') }}</div>
|
|
184
111
|
</div>
|
|
185
112
|
</div>
|
|
186
113
|
</div>
|
|
187
114
|
|
|
188
115
|
<div class="selector-section">
|
|
189
|
-
<div class="selector-header">
|
|
190
|
-
|
|
191
|
-
</div>
|
|
192
|
-
<button class="btn-tool" @click="openAgentsEditor" :disabled="loading || !!initError || agentsLoading">
|
|
193
|
-
{{ agentsLoading ? t('config.modelLoading') : t('config.agents.open') }}
|
|
194
|
-
</button>
|
|
195
|
-
</div>
|
|
196
|
-
|
|
197
|
-
<div class="selector-section">
|
|
198
|
-
<div class="selector-header">
|
|
199
|
-
<span class="selector-title">{{ t('config.health.title') }}</span>
|
|
200
|
-
</div>
|
|
201
|
-
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
202
|
-
{{ healthCheckLoading ? t('config.health.running') : t('config.health.run') }}
|
|
203
|
-
</button>
|
|
116
|
+
<div class="selector-header"><span class="selector-title">{{ t('config.health.title') }}</span></div>
|
|
117
|
+
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">{{ healthCheckLoading ? t('config.health.running') : t('config.health.run') }}</button>
|
|
204
118
|
<div class="config-template-hint">{{ t('config.health.hint') }}</div>
|
|
205
|
-
<div v-if="healthCheckLoading && healthCheckBatchTotal" class="config-template-hint">
|
|
206
|
-
{{ t('config.health.progress', { done: healthCheckBatchDone, total: healthCheckBatchTotal, failed: healthCheckBatchFailed }) }}
|
|
207
|
-
</div>
|
|
208
|
-
<div v-if="healthCheckResult && !healthCheckLoading" class="config-template-hint">
|
|
209
|
-
{{ healthCheckResult.ok ? t('config.health.ok') : t('config.health.fail') }} · {{ t('config.health.issues', { count: (healthCheckResult.issues || []).length }) }}
|
|
210
|
-
</div>
|
|
211
|
-
<button v-if="healthCheckResult && !healthCheckLoading" type="button" class="btn-mini" @click="showHealthCheckModal = true">
|
|
212
|
-
{{ t('common.detail') }}
|
|
213
|
-
</button>
|
|
214
|
-
<div v-if="healthCheckResult && !healthCheckLoading && (healthCheckResult.issues || []).length">
|
|
215
|
-
<div v-for="(issue, index) in healthCheckResult.issues" :key="issue.code || ('issue-' + index)" class="config-template-hint">
|
|
216
|
-
{{ issue.message || issue.code || '' }}<span v-if="issue.suggestion"> · {{ issue.suggestion }}</span>
|
|
217
|
-
</div>
|
|
218
|
-
</div>
|
|
219
119
|
</div>
|
|
220
|
-
|
|
221
120
|
</template>
|
|
222
121
|
|
|
223
122
|
<div v-if="!loading && !initError" class="card-list">
|
|
224
|
-
<div v-for="provider in displayProvidersList" :key="provider.name"
|
|
225
|
-
:class="['card', { active: displayCurrentProvider === provider.name }]"
|
|
226
|
-
@click="switchProvider(provider.name)"
|
|
227
|
-
@keydown.enter.self.prevent="switchProvider(provider.name)"
|
|
228
|
-
@keydown.space.self.prevent="switchProvider(provider.name)"
|
|
229
|
-
tabindex="0"
|
|
230
|
-
role="button"
|
|
231
|
-
:aria-current="displayCurrentProvider === provider.name ? 'true' : null">
|
|
123
|
+
<div v-for="provider in displayProvidersList" :key="provider.name" :class="['card', { active: displayCurrentProvider === provider.name }]" @click="switchProvider(provider.name)" @keydown.enter.self.prevent="switchProvider(provider.name)" @keydown.space.self.prevent="switchProvider(provider.name)" tabindex="0" role="button" :aria-current="displayCurrentProvider === provider.name ? 'true' : null">
|
|
232
124
|
<div class="card-leading">
|
|
233
125
|
<div class="card-icon">{{ provider.name.charAt(0).toUpperCase() }}<span v-if="isTransformProvider(provider)" class="card-icon-dot" title="通过内建转换适配"></span></div>
|
|
234
126
|
<div class="card-content">
|
|
@@ -236,33 +128,22 @@
|
|
|
236
128
|
<span>{{ provider.name }}</span>
|
|
237
129
|
<span v-if="provider.readOnly" class="provider-readonly-badge">{{ t('config.badge.system') }}</span>
|
|
238
130
|
</div>
|
|
239
|
-
<div v-if="provider.name !== 'local'" class="card-subtitle card-subtitle-model">
|
|
240
|
-
|
|
241
|
-
</div>
|
|
242
|
-
<div v-if="provider.name !== 'local'" class="card-subtitle card-subtitle-url">
|
|
243
|
-
{{ displayProviderUrl(provider) || t('config.url.unset') }}
|
|
244
|
-
</div>
|
|
131
|
+
<div v-if="provider.name !== 'local'" class="card-subtitle card-subtitle-model">{{ activeProviderModel(provider.name) || t('config.model.unset') }}</div>
|
|
132
|
+
<div v-if="provider.name !== 'local'" class="card-subtitle card-subtitle-url">{{ displayProviderUrl(provider) || t('config.url.unset') }}</div>
|
|
245
133
|
</div>
|
|
246
134
|
</div>
|
|
247
135
|
<div class="card-trailing">
|
|
248
|
-
<span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">
|
|
249
|
-
|
|
250
|
-
</span>
|
|
251
|
-
<span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">
|
|
252
|
-
{{ providerPillText(provider) }}
|
|
253
|
-
</span>
|
|
136
|
+
<span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">{{ formatLatency(speedResults[provider.name]) }}</span>
|
|
137
|
+
<span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">{{ providerPillText(provider) }}</span>
|
|
254
138
|
<div class="card-actions" @click.stop>
|
|
255
|
-
<button
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
264
|
-
<path d="M13 2L3 14h7l-1 8 12-14h-7l1-6z"/>
|
|
265
|
-
</svg>
|
|
139
|
+
<button class="card-action-btn" :class="{ loading: speedLoading[provider.name] }" :disabled="!!speedLoading[provider.name]" @click="runSpeedTest(provider.name, { silent: true })" :aria-label="t('config.availabilityTestAria', { name: provider.name })" :title="t('config.availabilityTest')">
|
|
140
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>
|
|
141
|
+
</button>
|
|
142
|
+
<button class="card-action-btn" :disabled="!shouldShowProviderEdit(provider)" @click="openEditModal(provider)" :aria-label="t('config.provider.edit.aria', { name: provider.name })" :title="shouldShowProviderEdit(provider) ? t('common.edit') : t('common.notEditable')">
|
|
143
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
|
144
|
+
</button>
|
|
145
|
+
<button v-if="!provider.readOnly" class="card-action-btn" @click="openCloneProviderModal(provider)" :aria-label="t('config.provider.clone.aria', { name: provider.name })" :title="t('config.provider.clone')">
|
|
146
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
266
147
|
</button>
|
|
267
148
|
<button
|
|
268
149
|
v-if="!provider.readOnly"
|
|
@@ -278,42 +159,8 @@
|
|
|
278
159
|
<path d="M12 2v14"/>
|
|
279
160
|
</svg>
|
|
280
161
|
</button>
|
|
281
|
-
<button
|
|
282
|
-
|
|
283
|
-
class="card-action-btn"
|
|
284
|
-
:class="{ disabled: !shouldShowProviderEdit(provider) }"
|
|
285
|
-
:disabled="!shouldShowProviderEdit(provider)"
|
|
286
|
-
@click="openEditModal(provider)"
|
|
287
|
-
:aria-label="t('config.provider.edit.aria', { name: provider.name })"
|
|
288
|
-
:title="shouldShowProviderEdit(provider) ? t('common.edit') : t('common.notEditable')">
|
|
289
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
290
|
-
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
291
|
-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
292
|
-
</svg>
|
|
293
|
-
</button>
|
|
294
|
-
<button
|
|
295
|
-
v-if="!provider.readOnly"
|
|
296
|
-
class="card-action-btn"
|
|
297
|
-
@click="openCloneProviderModal(provider)"
|
|
298
|
-
:aria-label="t('config.provider.clone.aria', { name: provider.name })"
|
|
299
|
-
:title="t('config.provider.clone')">
|
|
300
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
301
|
-
<rect x="9" y="9" width="13" height="13" rx="2"/>
|
|
302
|
-
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
303
|
-
</svg>
|
|
304
|
-
</button>
|
|
305
|
-
<button
|
|
306
|
-
v-if="!provider.readOnly"
|
|
307
|
-
class="card-action-btn delete"
|
|
308
|
-
:class="{ disabled: !shouldShowProviderDelete(provider) }"
|
|
309
|
-
:disabled="!shouldShowProviderDelete(provider)"
|
|
310
|
-
@click="deleteProvider(provider.name)"
|
|
311
|
-
:aria-label="t('config.provider.delete.aria', { name: provider.name })"
|
|
312
|
-
:title="shouldShowProviderDelete(provider) ? t('common.delete') : t('common.notDeletable')">
|
|
313
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
314
|
-
<path d="M3 6h18"/>
|
|
315
|
-
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
|
316
|
-
</svg>
|
|
162
|
+
<button v-if="!provider.readOnly" class="card-action-btn delete" :class="{ disabled: !shouldShowProviderDelete(provider) }" :disabled="!shouldShowProviderDelete(provider)" @click="deleteProvider(provider.name)" :aria-label="t('config.provider.delete.aria', { name: provider.name })" :title="shouldShowProviderDelete(provider) ? t('common.delete') : t('common.notDeletable')">
|
|
163
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
|
317
164
|
</button>
|
|
318
165
|
</div>
|
|
319
166
|
</div>
|
|
@@ -323,35 +170,22 @@
|
|
|
323
170
|
<div v-if="displayCurrentProvider === 'local'" class="bridge-pool-panel">
|
|
324
171
|
<div class="bridge-pool-header">
|
|
325
172
|
<span class="bridge-pool-icon">
|
|
326
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
327
|
-
<circle cx="6" cy="6" r="2"/>
|
|
328
|
-
<circle cx="18" cy="6" r="2"/>
|
|
329
|
-
<circle cx="12" cy="18" r="2"/>
|
|
330
|
-
<path d="M6 8v4h6v4"/>
|
|
331
|
-
<path d="M18 8v4h-6v4"/>
|
|
332
|
-
</svg>
|
|
173
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M6 8v4h6v4"/><path d="M18 8v4h-6v4"/></svg>
|
|
333
174
|
</span>
|
|
334
175
|
<span class="bridge-pool-title">轮询池</span>
|
|
335
176
|
<span class="bridge-pool-hint">勾选参与负载均衡的提供商</span>
|
|
336
177
|
</div>
|
|
337
178
|
<div v-if="localBridgeCandidateProviders().length === 0" class="bridge-pool-empty">
|
|
338
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="16" height="16">
|
|
339
|
-
<path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"/>
|
|
340
|
-
</svg>
|
|
179
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="16" height="16"><path d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0z"/></svg>
|
|
341
180
|
<span>暂无可用上游 provider,请先添加直连 provider</span>
|
|
342
181
|
</div>
|
|
343
182
|
<div v-else class="bridge-pool-list">
|
|
344
183
|
<label v-for="cp in localBridgeCandidateProviders()" :key="cp.name" class="bridge-pool-item">
|
|
345
184
|
<span class="bridge-pool-item-name">{{ cp.name }}</span>
|
|
346
|
-
<span class="bridge-pool-item-status" :class="{ active: !isLocalBridgeExcluded(cp.name) }">
|
|
347
|
-
|
|
348
|
-
</span>
|
|
349
|
-
<input type="checkbox"
|
|
350
|
-
:checked="!isLocalBridgeExcluded(cp.name)"
|
|
351
|
-
@change="toggleLocalBridgeExcluded(cp.name)" />
|
|
185
|
+
<span class="bridge-pool-item-status" :class="{ active: !isLocalBridgeExcluded(cp.name) }">{{ isLocalBridgeExcluded(cp.name) ? '未启用' : '已启用' }}</span>
|
|
186
|
+
<input type="checkbox" :checked="!isLocalBridgeExcluded(cp.name)" @change="toggleLocalBridgeExcluded(cp.name)" />
|
|
352
187
|
</label>
|
|
353
188
|
</div>
|
|
354
189
|
</div>
|
|
355
|
-
|
|
356
190
|
</template>
|
|
357
|
-
</div>
|
|
191
|
+
</div>
|
|
@@ -119,39 +119,6 @@
|
|
|
119
119
|
<strong>{{ inspectorModelLoadStatus }}</strong>
|
|
120
120
|
</div>
|
|
121
121
|
</div>
|
|
122
|
-
<div v-if="providersHealthLoading || providersHealthResult" class="doctor-providers-health">
|
|
123
|
-
<div class="doctor-providers-health-title">
|
|
124
|
-
{{ t('dashboard.providersHealth.title') }}
|
|
125
|
-
<span v-if="providersHealthResult" class="doctor-providers-health-summary">
|
|
126
|
-
{{ providersHealthResult.summary.green }}/{{ providersHealthResult.summary.total }}
|
|
127
|
-
</span>
|
|
128
|
-
<span v-else class="doctor-providers-health-summary">{{ t('dashboard.providersHealth.checking') }}</span>
|
|
129
|
-
</div>
|
|
130
|
-
<div v-if="providersHealthResult" class="doctor-providers-grid">
|
|
131
|
-
<div v-for="entry in providersHealthResult.providers"
|
|
132
|
-
:key="entry.provider"
|
|
133
|
-
class="doctor-provider-chip"
|
|
134
|
-
:class="[entry.status]"
|
|
135
|
-
:title="entry.provider + (entry.remote ? (entry.remote.ok ? ' OK' : ' ' + entry.remote.message) : '')">
|
|
136
|
-
<span class="doctor-provider-name">
|
|
137
|
-
{{ entry.provider }}
|
|
138
|
-
<span v-if="entry.isCurrent" class="doctor-provider-current">{{ t('dashboard.providersHealth.current') }}</span>
|
|
139
|
-
</span>
|
|
140
|
-
<span class="doctor-provider-status-dot"></span>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
<div v-if="providersHealthResult && providersHealthResult.providers.some(e => e.issues && e.issues.length)" class="doctor-provider-issues">
|
|
144
|
-
<div v-for="entry in providersHealthResult.providers.filter(e => e.issues && e.issues.length)"
|
|
145
|
-
:key="entry.provider + '-issues'"
|
|
146
|
-
class="doctor-provider-issue-card">
|
|
147
|
-
<div class="doctor-provider-issue-name">{{ entry.provider }}</div>
|
|
148
|
-
<div v-for="issue in entry.issues" :key="issue.code" class="doctor-provider-issue-row">
|
|
149
|
-
<span class="doctor-provider-issue-text">{{ issue.message }}</span>
|
|
150
|
-
<span v-if="issue.suggestion" class="doctor-provider-issue-fix">{{ issue.suggestion }}</span>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
122
|
<div v-if="healthCheckResult" class="doctor-health-result" :class="healthCheckResult.ok ? 'ok' : 'error'">
|
|
156
123
|
<div class="doctor-health-title">
|
|
157
124
|
{{ healthCheckResult.ok ? t('dashboard.health.ok') : t('dashboard.health.fail') }}
|
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
id="panel-docs"
|
|
5
5
|
role="tabpanel"
|
|
6
6
|
aria-labelledby="tab-docs">
|
|
7
|
-
<div class="selector-section
|
|
8
|
-
<div class="selector-header
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
<div class="skills-panel-note docs-section-note">{{ t('docs.subtitle') }}</div>
|
|
12
|
-
</div>
|
|
7
|
+
<div class="selector-section">
|
|
8
|
+
<div class="selector-header">
|
|
9
|
+
<span class="selector-title">{{ t('docs.title') }}</span>
|
|
10
|
+
<div class="skills-panel-note docs-section-note">{{ t('docs.subtitle') }}</div>
|
|
13
11
|
</div>
|
|
14
12
|
|
|
15
13
|
<div class="docs-toolbar-grid">
|
|
@@ -21,7 +19,6 @@
|
|
|
21
19
|
<option value="bun">bun</option>
|
|
22
20
|
</select>
|
|
23
21
|
</div>
|
|
24
|
-
|
|
25
22
|
<div class="docs-toolbar-card docs-toolbar-card-wide">
|
|
26
23
|
<label class="form-label">{{ t('common.mirror') }}</label>
|
|
27
24
|
<div class="install-action-tabs">
|
|
@@ -30,19 +27,10 @@
|
|
|
30
27
|
<button type="button" class="btn-mini" :class="{ active: installRegistryPreset === 'tencent' }" @click="setInstallRegistryPreset('tencent')">{{ t('docs.registry.tencent') }}</button>
|
|
31
28
|
<button type="button" class="btn-mini" :class="{ active: installRegistryPreset === 'custom' }" @click="setInstallRegistryPreset('custom')">{{ t('common.custom') }}</button>
|
|
32
29
|
</div>
|
|
33
|
-
<input
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class="form-input install-registry-input"
|
|
37
|
-
placeholder="https://registry.example.com">
|
|
38
|
-
<div class="form-hint install-registry-hint" v-if="installRegistryPreview">
|
|
39
|
-
{{ t('docs.registryHintPrefix') }} --registry={{ installRegistryPreview }}
|
|
40
|
-
</div>
|
|
41
|
-
<div class="form-hint install-registry-hint" v-else-if="installRegistryPreset === 'custom'">
|
|
42
|
-
{{ t('docs.registryHintCustom') }}
|
|
43
|
-
</div>
|
|
30
|
+
<input v-if="installRegistryPreset === 'custom'" v-model="installRegistryCustom" class="form-input install-registry-input" placeholder="https://registry.example.com">
|
|
31
|
+
<div class="form-hint install-registry-hint" v-if="installRegistryPreview">{{ t('docs.registryHintPrefix') }} --registry={{ installRegistryPreview }}</div>
|
|
32
|
+
<div class="form-hint install-registry-hint" v-else-if="installRegistryPreset === 'custom'">{{ t('docs.registryHintCustom') }}</div>
|
|
44
33
|
</div>
|
|
45
|
-
|
|
46
34
|
<div class="docs-toolbar-card docs-toolbar-card-wide">
|
|
47
35
|
<label class="form-label">{{ t('common.action') }}</label>
|
|
48
36
|
<div class="install-action-tabs">
|
|
@@ -62,30 +50,21 @@
|
|
|
62
50
|
<span class="docs-summary-label">{{ t('common.currentPm') }}</span>
|
|
63
51
|
<strong class="docs-summary-value">{{ String(installPackageManager || 'npm').toUpperCase() }}</strong>
|
|
64
52
|
</div>
|
|
65
|
-
<div class="docs-summary-item">
|
|
66
|
-
<span class="docs-summary-label">{{ t('common.currentAction') }}</span>
|
|
67
|
-
<strong class="docs-summary-value">{{ installCommandAction === 'update' ? t('common.update') : (installCommandAction === 'uninstall' ? t('common.uninstall') : t('common.install')) }}</strong>
|
|
68
|
-
</div>
|
|
69
53
|
<div class="docs-summary-item docs-summary-item-wide">
|
|
70
|
-
<span class="docs-summary-label">{{ t('common.
|
|
54
|
+
<span class="docs-summary-label">{{ t('common.registry') }}</span>
|
|
71
55
|
<strong class="docs-summary-value">{{ installRegistryPreview || t('common.defaultOfficial') }}</strong>
|
|
72
56
|
</div>
|
|
73
57
|
</div>
|
|
74
58
|
</div>
|
|
75
59
|
|
|
76
|
-
<div class="selector-section
|
|
77
|
-
<div class="selector-header
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
<div class="skills-panel-note docs-section-note">{{ t('docs.section.commandsNote') }}</div>
|
|
81
|
-
</div>
|
|
60
|
+
<div class="selector-section">
|
|
61
|
+
<div class="selector-header">
|
|
62
|
+
<span class="selector-title">{{ t('docs.section.commands') }}</span>
|
|
63
|
+
<div class="skills-panel-note docs-section-note">{{ t('docs.section.commandsNote') }}</div>
|
|
82
64
|
</div>
|
|
83
65
|
|
|
84
66
|
<div class="install-list docs-install-list">
|
|
85
|
-
<div
|
|
86
|
-
class="install-row docs-install-row"
|
|
87
|
-
v-for="target in installTargetCards"
|
|
88
|
-
:key="'docs-install-command-' + target.id + '-' + installCommandAction">
|
|
67
|
+
<div class="install-row docs-install-row" v-for="target in installTargetCards" :key="'docs-install-command-' + target.id + '-' + installCommandAction">
|
|
89
68
|
<div class="install-row-main">
|
|
90
69
|
<div class="docs-command-head">
|
|
91
70
|
<div class="install-row-title">{{ target.name }}</div>
|
|
@@ -97,22 +76,13 @@
|
|
|
97
76
|
<div class="docs-command-row">
|
|
98
77
|
<div class="docs-command-box" role="group" :aria-label="t('docs.command.aria', { name: target.name })">
|
|
99
78
|
<code class="install-command">{{ target.command }}</code>
|
|
100
|
-
<button
|
|
101
|
-
type="button"
|
|
102
|
-
class="btn-mini docs-copy-btn"
|
|
103
|
-
:disabled="!target.command"
|
|
104
|
-
@click="copyInstallCommand(target.command)">{{ t('common.copy') }}</button>
|
|
79
|
+
<button type="button" class="btn-mini docs-copy-btn" :disabled="!target.command" @click="copyInstallCommand(target.command)">{{ t('common.copy') }}</button>
|
|
105
80
|
</div>
|
|
106
81
|
</div>
|
|
107
|
-
<div
|
|
108
|
-
v-if="target.id === 'codex' && target.termuxCommand && target.termuxCommand !== target.command"
|
|
109
|
-
class="docs-command-row docs-command-row-secondary">
|
|
82
|
+
<div v-if="target.id === 'codex' && target.termuxCommand && target.termuxCommand !== target.command" class="docs-command-row docs-command-row-secondary">
|
|
110
83
|
<div class="docs-command-box" role="group" :aria-label="t('docs.termuxAria')">
|
|
111
84
|
<code class="install-command"><span class="docs-command-prefix">{{ t('docs.termuxLabel') }}</span>{{ target.termuxCommand }}</code>
|
|
112
|
-
<button
|
|
113
|
-
type="button"
|
|
114
|
-
class="btn-mini docs-copy-btn"
|
|
115
|
-
@click="copyInstallCommand(target.termuxCommand)">{{ t('common.copy') }}</button>
|
|
85
|
+
<button type="button" class="btn-mini docs-copy-btn" @click="copyInstallCommand(target.termuxCommand)">{{ t('common.copy') }}</button>
|
|
116
86
|
</div>
|
|
117
87
|
</div>
|
|
118
88
|
</div>
|
|
@@ -120,12 +90,10 @@
|
|
|
120
90
|
</div>
|
|
121
91
|
</div>
|
|
122
92
|
|
|
123
|
-
<div class="selector-section
|
|
124
|
-
<div class="selector-header
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
<div class="skills-panel-note docs-section-note">{{ t('docs.section.faqNote') }}</div>
|
|
128
|
-
</div>
|
|
93
|
+
<div class="selector-section">
|
|
94
|
+
<div class="selector-header">
|
|
95
|
+
<span class="selector-title">{{ t('docs.section.faq') }}</span>
|
|
96
|
+
<div class="skills-panel-note docs-section-note">{{ t('docs.section.faqNote') }}</div>
|
|
129
97
|
</div>
|
|
130
98
|
|
|
131
99
|
<div class="docs-help-grid">
|
|
@@ -144,4 +112,4 @@
|
|
|
144
112
|
</div>
|
|
145
113
|
</div>
|
|
146
114
|
</div>
|
|
147
|
-
</div>
|
|
115
|
+
</div>
|