codexmate 0.0.22 → 0.0.24
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.zh.md +8 -5
- package/cli/auth-profiles.js +23 -7
- package/cli/doctor-core.js +903 -0
- package/cli/import-skills-url.js +334 -0
- package/cli.js +304 -208
- package/lib/cli-models-utils.js +0 -40
- package/lib/cli-network-utils.js +28 -2
- package/package.json +5 -2
- package/plugins/README.md +20 -0
- package/plugins/README.zh-CN.md +20 -0
- package/plugins/prompt-templates/comment-polish/index.mjs +25 -0
- package/plugins/prompt-templates/computed.mjs +253 -0
- package/plugins/prompt-templates/index.mjs +8 -0
- package/plugins/prompt-templates/manifest.mjs +15 -0
- package/plugins/prompt-templates/methods.mjs +619 -0
- package/plugins/prompt-templates/overview.mjs +90 -0
- package/plugins/prompt-templates/ownership.mjs +19 -0
- package/plugins/prompt-templates/rule-ack/index.mjs +21 -0
- package/plugins/prompt-templates/storage.mjs +64 -0
- package/plugins/registry.mjs +16 -0
- package/res/logo-pack.webp +0 -0
- package/web-ui/app.js +68 -34
- package/web-ui/index.html +4 -3
- package/web-ui/modules/app.computed.dashboard.mjs +22 -22
- package/web-ui/modules/app.computed.main-tabs.mjs +9 -2
- package/web-ui/modules/app.methods.agents.mjs +91 -3
- package/web-ui/modules/app.methods.codex-config.mjs +153 -164
- package/web-ui/modules/app.methods.install.mjs +16 -0
- package/web-ui/modules/app.methods.navigation.mjs +76 -0
- package/web-ui/modules/app.methods.runtime.mjs +24 -2
- package/web-ui/modules/app.methods.session-browser.mjs +73 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +12 -0
- package/web-ui/modules/app.methods.task-orchestration.mjs +96 -11
- package/web-ui/modules/config-mode.computed.mjs +1 -3
- package/web-ui/modules/i18n.dict.mjs +2039 -0
- package/web-ui/modules/i18n.mjs +2 -1555
- package/web-ui/modules/plugins.computed.mjs +2 -219
- package/web-ui/modules/plugins.methods.mjs +2 -619
- package/web-ui/modules/plugins.storage.mjs +11 -37
- package/web-ui/modules/sessions-filters-url.mjs +85 -0
- package/web-ui/partials/index/layout-header.html +38 -34
- package/web-ui/partials/index/modal-config-template-agents.html +3 -4
- package/web-ui/partials/index/modal-health-check.html +33 -60
- package/web-ui/partials/index/panel-config-claude.html +56 -15
- package/web-ui/partials/index/panel-config-codex.html +68 -19
- package/web-ui/partials/index/panel-config-openclaw.html +8 -3
- package/web-ui/partials/index/panel-dashboard.html +186 -0
- package/web-ui/partials/index/panel-docs.html +1 -1
- package/web-ui/partials/index/panel-market.html +3 -0
- package/web-ui/partials/index/panel-orchestration.html +105 -111
- package/web-ui/partials/index/panel-plugins.html +48 -12
- package/web-ui/partials/index/panel-sessions.html +12 -3
- package/web-ui/partials/index/panel-settings.html +1 -1
- package/web-ui/partials/index/panel-usage.html +7 -6
- package/web-ui/styles/controls-forms.css +16 -2
- package/web-ui/styles/dashboard.css +274 -0
- package/web-ui/styles/layout-shell.css +11 -5
- package/web-ui/styles/navigation-panels.css +8 -0
- package/web-ui/styles/plugins-panel.css +5 -0
- package/web-ui/styles/sessions-list.css +3 -3
- package/web-ui/styles/sessions-usage.css +37 -0
- package/web-ui/styles/skills-market.css +12 -2
- package/web-ui/styles/task-orchestration.css +57 -11
- package/web-ui/styles.css +1 -0
- package/res/logo.png +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildSessionFilterCacheState,
|
|
3
|
+
isSessionQueryEnabled
|
|
4
|
+
} from '../logic.mjs';
|
|
5
|
+
|
|
6
|
+
export function normalizeSessionRoleFilter(value) {
|
|
7
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
8
|
+
if (normalized === 'user' || normalized === 'assistant' || normalized === 'system') {
|
|
9
|
+
return normalized;
|
|
10
|
+
}
|
|
11
|
+
return 'all';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeSessionTimePreset(value) {
|
|
15
|
+
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
16
|
+
if (normalized === '7d' || normalized === '30d' || normalized === '90d') {
|
|
17
|
+
return normalized;
|
|
18
|
+
}
|
|
19
|
+
return 'all';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function readSessionsFilterUrlState() {
|
|
23
|
+
try {
|
|
24
|
+
const url = new URL(window.location.href);
|
|
25
|
+
if (url.pathname === '/session') return null;
|
|
26
|
+
const source = (url.searchParams.get('s_source') || '').trim().toLowerCase();
|
|
27
|
+
const pathFilter = url.searchParams.get('s_path') || '';
|
|
28
|
+
const query = url.searchParams.get('s_query') || '';
|
|
29
|
+
const roleFilter = url.searchParams.get('s_role') || 'all';
|
|
30
|
+
const timeRangePreset = url.searchParams.get('s_time') || 'all';
|
|
31
|
+
const hasAny = !!(source || pathFilter || query || roleFilter !== 'all' || timeRangePreset !== 'all');
|
|
32
|
+
if (!hasAny) return null;
|
|
33
|
+
return {
|
|
34
|
+
source: source || 'all',
|
|
35
|
+
pathFilter,
|
|
36
|
+
query,
|
|
37
|
+
roleFilter,
|
|
38
|
+
timeRangePreset
|
|
39
|
+
};
|
|
40
|
+
} catch (_) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function applySessionsFilterUrlState(vm, state) {
|
|
46
|
+
if (!vm || !state) return;
|
|
47
|
+
const cached = buildSessionFilterCacheState(state.source, state.pathFilter);
|
|
48
|
+
vm.sessionFilterSource = cached.source;
|
|
49
|
+
vm.sessionPathFilter = cached.pathFilter;
|
|
50
|
+
vm.sessionQuery = typeof state.query === 'string' ? state.query : '';
|
|
51
|
+
vm.sessionRoleFilter = normalizeSessionRoleFilter(state.roleFilter);
|
|
52
|
+
vm.sessionTimePreset = normalizeSessionTimePreset(state.timeRangePreset);
|
|
53
|
+
if (typeof vm.refreshSessionPathOptions === 'function') {
|
|
54
|
+
vm.refreshSessionPathOptions(vm.sessionFilterSource);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function buildSessionsFilterShareUrl(vm) {
|
|
59
|
+
try {
|
|
60
|
+
const url = new URL(window.location.href);
|
|
61
|
+
if (url.pathname === '/session') return '';
|
|
62
|
+
url.searchParams.set('s_source', String(vm.sessionFilterSource || 'all'));
|
|
63
|
+
if (vm.sessionPathFilter) url.searchParams.set('s_path', String(vm.sessionPathFilter || ''));
|
|
64
|
+
else url.searchParams.delete('s_path');
|
|
65
|
+
if (vm.sessionQuery && isSessionQueryEnabled(vm.sessionFilterSource)) url.searchParams.set('s_query', String(vm.sessionQuery || ''));
|
|
66
|
+
else url.searchParams.delete('s_query');
|
|
67
|
+
if (vm.sessionRoleFilter && vm.sessionRoleFilter !== 'all') url.searchParams.set('s_role', String(vm.sessionRoleFilter || 'all'));
|
|
68
|
+
else url.searchParams.delete('s_role');
|
|
69
|
+
if (vm.sessionTimePreset && vm.sessionTimePreset !== 'all') url.searchParams.set('s_time', String(vm.sessionTimePreset || 'all'));
|
|
70
|
+
else url.searchParams.delete('s_time');
|
|
71
|
+
url.searchParams.set('tab', 'sessions');
|
|
72
|
+
return url.toString();
|
|
73
|
+
} catch (_) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function syncSessionsFilterUrl(vm) {
|
|
79
|
+
const url = buildSessionsFilterShareUrl(vm);
|
|
80
|
+
if (!url) return;
|
|
81
|
+
try {
|
|
82
|
+
window.history.replaceState(null, '', url);
|
|
83
|
+
} catch (_) {}
|
|
84
|
+
}
|
|
85
|
+
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
<div id="app" class="container" v-cloak>
|
|
2
|
-
<div v-if="!sessionStandalone" class="top-tabs" role="tablist" aria-label="
|
|
2
|
+
<div v-if="!sessionStandalone" class="top-tabs" role="tablist" :aria-label="t('nav.topTabs.aria')">
|
|
3
|
+
<button class="top-tab"
|
|
4
|
+
id="tab-dashboard"
|
|
5
|
+
role="tab"
|
|
6
|
+
data-main-tab="dashboard"
|
|
7
|
+
:tabindex="mainTab === 'dashboard' ? 0 : -1"
|
|
8
|
+
:aria-selected="mainTab === 'dashboard'"
|
|
9
|
+
aria-controls="panel-dashboard"
|
|
10
|
+
:class="{ active: isMainTabNavActive('dashboard') }"
|
|
11
|
+
@pointerdown="onMainTabPointerDown('dashboard', $event)"
|
|
12
|
+
@click="onMainTabClick('dashboard', $event)">{{ t('tab.dashboard') }}</button>
|
|
3
13
|
<button class="top-tab"
|
|
4
14
|
id="tab-docs"
|
|
5
15
|
role="tab"
|
|
@@ -11,38 +21,16 @@
|
|
|
11
21
|
@pointerdown="onMainTabPointerDown('docs', $event)"
|
|
12
22
|
@click="onMainTabClick('docs', $event)">{{ t('tab.docs') }}</button>
|
|
13
23
|
<button class="top-tab"
|
|
14
|
-
id="tab-config
|
|
24
|
+
id="tab-config"
|
|
15
25
|
role="tab"
|
|
16
26
|
data-main-tab="config"
|
|
17
|
-
data-config-mode="
|
|
18
|
-
:tabindex="mainTab === 'config'
|
|
19
|
-
:aria-selected="mainTab === 'config'
|
|
20
|
-
aria-controls="panel-config-provider"
|
|
21
|
-
:class="{ active:
|
|
22
|
-
@pointerdown="
|
|
23
|
-
@click="
|
|
24
|
-
<button class="top-tab"
|
|
25
|
-
id="tab-config-claude"
|
|
26
|
-
role="tab"
|
|
27
|
-
data-main-tab="config"
|
|
28
|
-
data-config-mode="claude"
|
|
29
|
-
:tabindex="mainTab === 'config' && configMode === 'claude' ? 0 : -1"
|
|
30
|
-
:aria-selected="mainTab === 'config' && configMode === 'claude'"
|
|
31
|
-
aria-controls="panel-config-claude"
|
|
32
|
-
:class="{ active: isConfigModeNavActive('claude') }"
|
|
33
|
-
@pointerdown="onConfigTabPointerDown('claude', $event)"
|
|
34
|
-
@click="onConfigTabClick('claude', $event)">{{ t('tab.config.claude') }}</button>
|
|
35
|
-
<button class="top-tab"
|
|
36
|
-
id="tab-config-openclaw"
|
|
37
|
-
role="tab"
|
|
38
|
-
data-main-tab="config"
|
|
39
|
-
data-config-mode="openclaw"
|
|
40
|
-
:tabindex="mainTab === 'config' && configMode === 'openclaw' ? 0 : -1"
|
|
41
|
-
:aria-selected="mainTab === 'config' && configMode === 'openclaw'"
|
|
42
|
-
aria-controls="panel-config-openclaw"
|
|
43
|
-
:class="{ active: isConfigModeNavActive('openclaw') }"
|
|
44
|
-
@pointerdown="onConfigTabPointerDown('openclaw', $event)"
|
|
45
|
-
@click="onConfigTabClick('openclaw', $event)">{{ t('tab.config.openclaw') }}</button>
|
|
27
|
+
:data-config-mode="configMode"
|
|
28
|
+
:tabindex="mainTab === 'config' ? 0 : -1"
|
|
29
|
+
:aria-selected="mainTab === 'config'"
|
|
30
|
+
:aria-controls="configMode === 'claude' ? 'panel-config-claude' : (configMode === 'openclaw' ? 'panel-config-openclaw' : 'panel-config-provider')"
|
|
31
|
+
:class="{ active: isMainTabNavActive('config') }"
|
|
32
|
+
@pointerdown="onMainTabPointerDown('config', $event)"
|
|
33
|
+
@click="onMainTabClick('config', $event)">{{ t('tab.config') }}</button>
|
|
46
34
|
<button class="top-tab"
|
|
47
35
|
id="tab-sessions"
|
|
48
36
|
role="tab"
|
|
@@ -126,16 +114,32 @@
|
|
|
126
114
|
<aside class="side-rail" v-if="!sessionStandalone">
|
|
127
115
|
<div class="brand-block">
|
|
128
116
|
<div class="brand-head">
|
|
129
|
-
<img class="brand-logo" src="/res/logo.
|
|
117
|
+
<img class="brand-logo" src="/res/logo-pack.webp" alt="Codex Mate logo">
|
|
130
118
|
<div class="brand-copy">
|
|
131
|
-
<div class="brand-kicker">
|
|
119
|
+
<div class="brand-kicker">{{ t('brand.kicker.workspace') }}</div>
|
|
132
120
|
<div class="brand-title">Codex Mate</div>
|
|
133
121
|
</div>
|
|
134
122
|
</div>
|
|
135
|
-
<div class="brand-subtitle">
|
|
123
|
+
<div class="brand-subtitle">{{ t('brand.subtitle.localConfigSessionsWorkspace') }}</div>
|
|
136
124
|
</div>
|
|
137
125
|
|
|
138
126
|
<div class="side-rail-nav">
|
|
127
|
+
<div class="side-section" role="navigation" :aria-label="t('side.overview')">
|
|
128
|
+
<div class="side-section-title">{{ t('side.overview') }}</div>
|
|
129
|
+
<button
|
|
130
|
+
id="side-tab-dashboard"
|
|
131
|
+
data-main-tab="dashboard"
|
|
132
|
+
:aria-current="mainTab === 'dashboard' ? 'page' : null"
|
|
133
|
+
:class="['side-item', { active: isMainTabNavActive('dashboard') }]"
|
|
134
|
+
@pointerdown="onMainTabPointerDown('dashboard', $event)"
|
|
135
|
+
@click="onMainTabClick('dashboard', $event)">
|
|
136
|
+
<div class="side-item-title">{{ t('side.overview.doctor') }}</div>
|
|
137
|
+
<div class="side-item-meta">
|
|
138
|
+
<span>{{ t('side.overview.doctor.meta') }}</span>
|
|
139
|
+
<span>{{ inspectorHealthStatus }}</span>
|
|
140
|
+
</div>
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
139
143
|
<div class="side-section" role="navigation" :aria-label="t('side.docs')">
|
|
140
144
|
<div class="side-section-title">{{ t('side.docs') }}</div>
|
|
141
145
|
<button
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
<div class="form-group">
|
|
110
|
-
<label class="form-label">{{ t('modal.agents.contentLabel') }}</label>
|
|
110
|
+
<label class="form-label">{{ t(agentsContext === 'claude-md' ? 'modal.agents.contentLabel.claudeMd' : 'modal.agents.contentLabel') }}</label>
|
|
111
111
|
<div
|
|
112
112
|
v-if="!agentsLoading && (hasAgentsContentChanged() || agentsDiffVisible)"
|
|
113
113
|
class="agents-diff-save-alert">
|
|
@@ -137,13 +137,12 @@
|
|
|
137
137
|
</div>
|
|
138
138
|
</div>
|
|
139
139
|
<textarea
|
|
140
|
-
v-else
|
|
141
140
|
v-model="agentsContent"
|
|
142
141
|
class="form-input template-editor"
|
|
143
142
|
spellcheck="false"
|
|
144
|
-
:readonly="agentsLoading || agentsSaving"
|
|
143
|
+
:readonly="agentsLoading || agentsSaving || agentsDiffVisible"
|
|
145
144
|
@input="onAgentsContentInput"
|
|
146
|
-
:placeholder="t('modal.agents.placeholder')"></textarea>
|
|
145
|
+
:placeholder="t(agentsContext === 'claude-md' ? 'modal.agents.placeholder.claudeMd' : 'modal.agents.placeholder')"></textarea>
|
|
147
146
|
<div class="template-editor-warning">
|
|
148
147
|
{{ agentsModalHint }}
|
|
149
148
|
<div class="agents-diff-hint">{{ t('modal.agents.hint.shortcuts') }}</div>
|
|
@@ -1,72 +1,45 @@
|
|
|
1
|
-
<div v-if="
|
|
2
|
-
<div class="modal
|
|
3
|
-
<div class="modal-
|
|
4
|
-
<div>
|
|
5
|
-
<div class="modal-title" id="health-check-dialog-title">{{ t('modal.healthCheck.title') }}</div>
|
|
6
|
-
<div class="health-check-dialog-subtitle">{{ t('modal.healthCheck.subtitle') }}</div>
|
|
7
|
-
</div>
|
|
8
|
-
<div v-if="healthCheckDialogLockedProvider" class="health-check-dialog-lock">
|
|
9
|
-
{{ t('modal.healthCheck.lockedPrefix', { value: healthCheckDialogLockedProvider }) }}
|
|
10
|
-
</div>
|
|
11
|
-
</div>
|
|
1
|
+
<div v-if="showHealthCheckModal" class="modal-overlay" @click.self="showHealthCheckModal = false">
|
|
2
|
+
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="health-check-modal-title">
|
|
3
|
+
<div class="modal-title" id="health-check-modal-title">{{ t('config.health.title') }}</div>
|
|
12
4
|
|
|
13
|
-
<div class="
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class="form-select"
|
|
19
|
-
:disabled="!!healthCheckDialogLockedProvider || healthCheckDialogSending">
|
|
20
|
-
<option value="" disabled>{{ t('placeholder.selectProvider') }}</option>
|
|
21
|
-
<option v-for="provider in displayProvidersList" :key="'health-check-provider-' + provider.name" :value="provider.name">
|
|
22
|
-
{{ provider.name }}
|
|
23
|
-
</option>
|
|
24
|
-
</select>
|
|
5
|
+
<div v-if="!healthCheckResult" class="state-message">{{ t('common.notLoaded') }}</div>
|
|
6
|
+
<template v-else>
|
|
7
|
+
<div class="form-hint">
|
|
8
|
+
{{ healthCheckResult.ok ? t('config.health.ok') : t('config.health.fail') }}
|
|
9
|
+
<span v-if="healthCheckResult.issues">({{ t('config.health.issues', { count: healthCheckResult.issues.length }) }})</span>
|
|
25
10
|
</div>
|
|
26
|
-
<div v-if="healthCheckDialogLastResult" class="health-check-dialog-result" :class="healthCheckDialogLastResult.ok ? 'ok' : 'error'">
|
|
27
|
-
<span>{{ healthCheckDialogLastResult.ok ? t('modal.healthCheck.result.ok') : t('modal.healthCheck.result.fail') }}</span>
|
|
28
|
-
<span v-if="healthCheckDialogLastResult.model">{{ t('label.model') }}{{ healthCheckDialogLastResult.model }}</span>
|
|
29
|
-
<span v-if="healthCheckDialogLastResult.status">HTTP {{ healthCheckDialogLastResult.status }}</span>
|
|
30
|
-
<span v-if="healthCheckDialogLastResult.durationMs">{{ healthCheckDialogLastResult.durationMs }} ms</span>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
11
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{{
|
|
12
|
+
<div v-if="healthCheckResult.remote && healthCheckResult.remote.type === 'remote-health-check'" class="form-hint">
|
|
13
|
+
{{ healthCheckResult.remote.endpoint || '' }}
|
|
14
|
+
<span v-if="healthCheckResult.remote.statusCode"> · {{ healthCheckResult.remote.statusCode }}</span>
|
|
15
|
+
<span v-if="healthCheckResult.remote.message"> · {{ healthCheckResult.remote.message }}</span>
|
|
37
16
|
</div>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<span
|
|
46
|
-
<span v-if="
|
|
17
|
+
|
|
18
|
+
<div v-if="healthCheckResult.remote && healthCheckResult.remote.speedTests" class="model-list">
|
|
19
|
+
<div
|
|
20
|
+
v-for="(result, name) in healthCheckResult.remote.speedTests"
|
|
21
|
+
:key="'health-speed-' + name"
|
|
22
|
+
class="model-item"
|
|
23
|
+
>
|
|
24
|
+
<span>{{ name }}</span>
|
|
25
|
+
<span v-if="result && result.ok" class="latency ok">{{ formatLatency(result) }}</span>
|
|
26
|
+
<span v-else class="latency error">{{ (result && result.error) ? result.error : t('config.health.fail') }}</span>
|
|
47
27
|
</div>
|
|
48
|
-
<div class="health-check-message-text">{{ item.text }}</div>
|
|
49
|
-
<pre v-if="item.rawPreview" class="health-check-message-raw">{{ item.rawPreview }}</pre>
|
|
50
28
|
</div>
|
|
51
|
-
</div>
|
|
52
29
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</div>
|
|
30
|
+
<div v-if="healthCheckResult.issues && healthCheckResult.issues.length" class="model-list">
|
|
31
|
+
<div
|
|
32
|
+
v-for="(issue, index) in healthCheckResult.issues"
|
|
33
|
+
:key="issue.code || ('health-issue-' + index)"
|
|
34
|
+
class="model-item"
|
|
35
|
+
>
|
|
36
|
+
<span>{{ issue.message || issue.code || '' }}</span>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
64
40
|
|
|
65
41
|
<div class="btn-group">
|
|
66
|
-
<button class="btn btn-
|
|
67
|
-
<button class="btn btn-confirm" @click="sendHealthCheckDialogMessage" :disabled="healthCheckDialogSending">
|
|
68
|
-
{{ healthCheckDialogSending ? t('common.sending') : t('modal.healthCheck.send') }}
|
|
69
|
-
</button>
|
|
42
|
+
<button class="btn btn-confirm" @click="showHealthCheckModal = false">{{ t('common.close') }}</button>
|
|
70
43
|
</div>
|
|
71
44
|
</div>
|
|
72
45
|
</div>
|
|
@@ -4,7 +4,32 @@
|
|
|
4
4
|
class="mode-content mode-cards"
|
|
5
5
|
id="panel-config-claude"
|
|
6
6
|
role="tabpanel"
|
|
7
|
-
:aria-labelledby="'tab-config-claude'">
|
|
7
|
+
:aria-labelledby="forceCompactLayout ? 'tab-config' : 'side-tab-config-claude'">
|
|
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
|
+
</div>
|
|
13
|
+
<template v-if="shouldShowCliInstallPlaceholder('claude')">
|
|
14
|
+
<div class="selector-section">
|
|
15
|
+
<div class="empty-state">
|
|
16
|
+
<div class="empty-state-title">{{ t('cli.missing.title', { name: 'Claude' }) }}</div>
|
|
17
|
+
<div class="empty-state-subtitle">{{ t('cli.missing.subtitle', { name: 'Claude' }) }}</div>
|
|
18
|
+
<div class="docs-command-row">
|
|
19
|
+
<div class="docs-command-box" role="group" :aria-label="t('cli.missing.commandAria', { name: 'Claude' })">
|
|
20
|
+
<code class="install-command">{{ getInstallCommand('claude', 'install') }}</code>
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
class="btn-mini docs-copy-btn"
|
|
24
|
+
:disabled="!getInstallCommand('claude', 'install')"
|
|
25
|
+
@click="copyInstallCommand(getInstallCommand('claude', 'install'))">{{ t('common.copy') }}</button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<button type="button" class="btn-tool btn-tool-compact" @click="mainTab = 'docs'; setInstallCommandAction('install')">{{ t('cli.missing.openDocs') }}</button>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<template v-else>
|
|
8
33
|
<!-- 添加提供商按钮 -->
|
|
9
34
|
<button class="btn-add" @click="openClaudeConfigModal" v-if="!loading && !initError">
|
|
10
35
|
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -69,22 +94,37 @@
|
|
|
69
94
|
|
|
70
95
|
<div class="selector-section">
|
|
71
96
|
<div class="selector-header">
|
|
72
|
-
<span class="selector-title">
|
|
97
|
+
<span class="selector-title">CLAUDE.md</span>
|
|
73
98
|
</div>
|
|
74
|
-
<button class="btn-tool" @click="
|
|
75
|
-
{{
|
|
99
|
+
<button class="btn-tool" @click="openClaudeMdEditor" :disabled="loading || !!initError || agentsLoading">
|
|
100
|
+
{{ agentsLoading ? t('config.modelLoading') : t('claude.md.open') }}
|
|
76
101
|
</button>
|
|
102
|
+
<div class="config-template-hint">
|
|
103
|
+
{{ t('claude.md.hint') }}
|
|
104
|
+
</div>
|
|
77
105
|
</div>
|
|
78
106
|
|
|
79
107
|
<div class="selector-section">
|
|
80
108
|
<div class="selector-header">
|
|
81
|
-
<span class="selector-title">
|
|
109
|
+
<span class="selector-title">{{ t('claude.health.title') }}</span>
|
|
82
110
|
</div>
|
|
83
|
-
<button class="btn-tool" @click="
|
|
84
|
-
{{
|
|
111
|
+
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
112
|
+
{{ healthCheckLoading ? t('claude.health.running') : t('claude.health.run') }}
|
|
85
113
|
</button>
|
|
86
|
-
<div class="config-template-hint">
|
|
87
|
-
|
|
114
|
+
<div class="config-template-hint">{{ t('claude.health.hint') }}</div>
|
|
115
|
+
<div v-if="healthCheckLoading && healthCheckBatchTotal" class="config-template-hint">
|
|
116
|
+
{{ t('claude.health.progress', { done: healthCheckBatchDone, total: healthCheckBatchTotal, failed: healthCheckBatchFailed }) }}
|
|
117
|
+
</div>
|
|
118
|
+
<div v-if="healthCheckResult && !healthCheckLoading" class="config-template-hint">
|
|
119
|
+
{{ healthCheckResult.ok ? t('config.health.ok') : t('config.health.fail') }} · {{ t('config.health.issues', { count: (healthCheckResult.issues || []).length }) }}
|
|
120
|
+
</div>
|
|
121
|
+
<button v-if="healthCheckResult && !healthCheckLoading" type="button" class="btn-mini" @click="showHealthCheckModal = true">
|
|
122
|
+
{{ t('common.detail') }}
|
|
123
|
+
</button>
|
|
124
|
+
<div v-if="healthCheckResult && !healthCheckLoading && (healthCheckResult.issues || []).length">
|
|
125
|
+
<div v-for="(issue, index) in healthCheckResult.issues" :key="issue.code || ('issue-' + index)" class="config-template-hint">
|
|
126
|
+
{{ issue.message || issue.code || '' }}<span v-if="issue.suggestion"> · {{ issue.suggestion }}</span>
|
|
127
|
+
</div>
|
|
88
128
|
</div>
|
|
89
129
|
</div>
|
|
90
130
|
|
|
@@ -105,27 +145,27 @@
|
|
|
105
145
|
</div>
|
|
106
146
|
</div>
|
|
107
147
|
<div class="card-trailing">
|
|
108
|
-
<span :class="['pill', config.hasKey ? 'configured' : 'empty']">
|
|
109
|
-
{{ config.hasKey ? t('claude.configured') : t('claude.notConfigured') }}
|
|
110
|
-
</span>
|
|
111
148
|
<span v-if="claudeSpeedResults[name]" :class="['latency', claudeSpeedResults[name].ok ? 'ok' : 'error']">
|
|
112
149
|
{{ formatLatency(claudeSpeedResults[name]) }}
|
|
113
150
|
</span>
|
|
151
|
+
<span :class="['pill', config.hasKey ? 'configured' : 'empty']">
|
|
152
|
+
{{ config.hasKey ? t('claude.configured') : t('claude.notConfigured') }}
|
|
153
|
+
</span>
|
|
114
154
|
<div class="card-actions" @click.stop>
|
|
115
|
-
<button class="card-action-btn" @click="openEditConfigModal(name)" :aria-label="
|
|
155
|
+
<button class="card-action-btn" @click="openEditConfigModal(name)" :aria-label="t('claude.action.editAria', { name })" :title="t('claude.action.edit')">
|
|
116
156
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
117
157
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
118
158
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
119
159
|
</svg>
|
|
120
160
|
</button>
|
|
121
|
-
<button class="card-action-btn" :class="{ loading: claudeShareLoading[name] }" @click="copyClaudeShareCommand(name)" disabled :title="t('claude.action.shareDisabled')" aria-label="
|
|
161
|
+
<button class="card-action-btn" :class="{ loading: claudeShareLoading[name] }" @click="copyClaudeShareCommand(name)" disabled :title="t('claude.action.shareDisabled')" :aria-label="t('config.shareCommand.aria')">
|
|
122
162
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
123
163
|
<path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
|
|
124
164
|
<path d="M16 6l-4-4-4 4"/>
|
|
125
165
|
<path d="M12 2v14"/>
|
|
126
166
|
</svg>
|
|
127
167
|
</button>
|
|
128
|
-
<button class="card-action-btn delete" @click="deleteClaudeConfig(name)" :aria-label="
|
|
168
|
+
<button class="card-action-btn delete" @click="deleteClaudeConfig(name)" :aria-label="t('claude.action.deleteAria', { name })" :title="t('claude.action.delete')">
|
|
129
169
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
130
170
|
<path d="M3 6h18"/>
|
|
131
171
|
<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"/>
|
|
@@ -135,4 +175,5 @@
|
|
|
135
175
|
</div>
|
|
136
176
|
</div>
|
|
137
177
|
</div>
|
|
178
|
+
</template>
|
|
138
179
|
</div>
|
|
@@ -4,7 +4,32 @@
|
|
|
4
4
|
class="mode-content mode-cards"
|
|
5
5
|
id="panel-config-provider"
|
|
6
6
|
role="tabpanel"
|
|
7
|
-
:aria-labelledby="'tab-config-' + configMode">
|
|
7
|
+
:aria-labelledby="forceCompactLayout ? 'tab-config' : ('side-tab-config-' + configMode)">
|
|
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
|
+
</div>
|
|
13
|
+
<template v-if="isCodexConfigMode && shouldShowCliInstallPlaceholder('codex')">
|
|
14
|
+
<div class="selector-section">
|
|
15
|
+
<div class="empty-state">
|
|
16
|
+
<div class="empty-state-title">{{ t('cli.missing.title', { name: 'Codex' }) }}</div>
|
|
17
|
+
<div class="empty-state-subtitle">{{ t('cli.missing.subtitle', { name: 'Codex' }) }}</div>
|
|
18
|
+
<div class="docs-command-row">
|
|
19
|
+
<div class="docs-command-box" role="group" :aria-label="t('cli.missing.commandAria', { name: 'Codex' })">
|
|
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>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<button type="button" class="btn-tool btn-tool-compact" @click="mainTab = 'docs'; setInstallCommandAction('install')">{{ t('cli.missing.openDocs') }}</button>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<template v-else>
|
|
8
33
|
<!-- 添加提供商按钮 -->
|
|
9
34
|
<button class="btn-add" @click="showAddModal = true" v-if="!loading && !initError">
|
|
10
35
|
<svg class="icon" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2">
|
|
@@ -18,8 +43,8 @@
|
|
|
18
43
|
<div class="selector-header">
|
|
19
44
|
<span class="selector-title">{{ t('config.models') }}</span>
|
|
20
45
|
<div class="selector-actions">
|
|
21
|
-
<button class="btn-icon" @click="showModelModal = true" aria-label="
|
|
22
|
-
<button class="btn-icon" @click="showModelListModal = true" aria-label="
|
|
46
|
+
<button class="btn-icon" @click="showModelModal = true" :aria-label="t('modal.modelAdd.title')" :title="t('modal.modelAdd.title')" v-if="modelsSource === 'legacy'">+</button>
|
|
47
|
+
<button class="btn-icon" @click="showModelListModal = true" :aria-label="t('modal.modelManage.title')" :title="t('modal.modelManage.title')" v-if="modelsSource === 'legacy'">≡</button>
|
|
23
48
|
</div>
|
|
24
49
|
</div>
|
|
25
50
|
<select
|
|
@@ -66,7 +91,7 @@
|
|
|
66
91
|
</div>
|
|
67
92
|
<select class="model-select" v-model="serviceTier" @change="onServiceTierChange">
|
|
68
93
|
<option value="fast">{{ t('config.serviceTier.fast') }}</option>
|
|
69
|
-
<option value="standard">standard</option>
|
|
94
|
+
<option value="standard">{{ t('config.serviceTier.standard') }}</option>
|
|
70
95
|
</select>
|
|
71
96
|
<div class="config-template-hint">
|
|
72
97
|
{{ t('config.serviceTier.hint', { field: 'service_tier' }) }}
|
|
@@ -143,6 +168,30 @@
|
|
|
143
168
|
</button>
|
|
144
169
|
</div>
|
|
145
170
|
|
|
171
|
+
<div class="selector-section">
|
|
172
|
+
<div class="selector-header">
|
|
173
|
+
<span class="selector-title">{{ t('config.health.title') }}</span>
|
|
174
|
+
</div>
|
|
175
|
+
<button class="btn-tool" @click="runHealthCheck" :disabled="healthCheckLoading || loading || !!initError">
|
|
176
|
+
{{ healthCheckLoading ? t('config.health.running') : t('config.health.run') }}
|
|
177
|
+
</button>
|
|
178
|
+
<div class="config-template-hint">{{ t('config.health.hint') }}</div>
|
|
179
|
+
<div v-if="healthCheckLoading && healthCheckBatchTotal" class="config-template-hint">
|
|
180
|
+
{{ t('config.health.progress', { done: healthCheckBatchDone, total: healthCheckBatchTotal, failed: healthCheckBatchFailed }) }}
|
|
181
|
+
</div>
|
|
182
|
+
<div v-if="healthCheckResult && !healthCheckLoading" class="config-template-hint">
|
|
183
|
+
{{ healthCheckResult.ok ? t('config.health.ok') : t('config.health.fail') }} · {{ t('config.health.issues', { count: (healthCheckResult.issues || []).length }) }}
|
|
184
|
+
</div>
|
|
185
|
+
<button v-if="healthCheckResult && !healthCheckLoading" type="button" class="btn-mini" @click="showHealthCheckModal = true">
|
|
186
|
+
{{ t('common.detail') }}
|
|
187
|
+
</button>
|
|
188
|
+
<div v-if="healthCheckResult && !healthCheckLoading && (healthCheckResult.issues || []).length">
|
|
189
|
+
<div v-for="(issue, index) in healthCheckResult.issues" :key="issue.code || ('issue-' + index)" class="config-template-hint">
|
|
190
|
+
{{ issue.message || issue.code || '' }}<span v-if="issue.suggestion"> · {{ issue.suggestion }}</span>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
146
195
|
</template>
|
|
147
196
|
|
|
148
197
|
<div v-if="!loading && !initError" class="card-list">
|
|
@@ -167,24 +216,23 @@
|
|
|
167
216
|
</div>
|
|
168
217
|
</div>
|
|
169
218
|
<div class="card-trailing">
|
|
170
|
-
<span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">
|
|
171
|
-
{{ providerPillText(provider) }}
|
|
172
|
-
</span>
|
|
173
219
|
<span v-if="speedResults[provider.name]" :class="['latency', speedResults[provider.name].ok ? 'ok' : 'error']">
|
|
174
220
|
{{ formatLatency(speedResults[provider.name]) }}
|
|
175
221
|
</span>
|
|
222
|
+
<span :class="['pill', providerPillConfigured(provider) ? 'configured' : 'empty']">
|
|
223
|
+
{{ providerPillText(provider) }}
|
|
224
|
+
</span>
|
|
176
225
|
<div class="card-actions" @click.stop>
|
|
177
226
|
<button
|
|
178
227
|
class="card-action-btn"
|
|
179
|
-
|
|
180
|
-
:disabled="
|
|
181
|
-
|
|
182
|
-
:
|
|
228
|
+
:class="{ loading: speedLoading[provider.name] }"
|
|
229
|
+
:disabled="!!speedLoading[provider.name]"
|
|
230
|
+
@click="runSpeedTest(provider.name, { silent: true })"
|
|
231
|
+
:aria-label="t('config.availabilityTestAria', { name: provider.name })"
|
|
232
|
+
:title="t('config.availabilityTest')"
|
|
183
233
|
>
|
|
184
234
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
185
|
-
<path d="
|
|
186
|
-
<path d="M8 9h8"/>
|
|
187
|
-
<path d="M8 13h5"/>
|
|
235
|
+
<path d="M13 2L3 14h7l-1 8 12-14h-7l1-6z"/>
|
|
188
236
|
</svg>
|
|
189
237
|
</button>
|
|
190
238
|
<button
|
|
@@ -194,7 +242,7 @@
|
|
|
194
242
|
:disabled="providerShareLoading[provider.name] || !shouldAllowProviderShare(provider)"
|
|
195
243
|
@click="copyProviderShareCommand(provider)"
|
|
196
244
|
:title="shouldAllowProviderShare(provider) ? t('config.shareCommand') : t('config.shareDisabled')"
|
|
197
|
-
aria-label="
|
|
245
|
+
:aria-label="t('config.shareCommand.aria')">
|
|
198
246
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
199
247
|
<path d="M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7"/>
|
|
200
248
|
<path d="M16 6l-4-4-4 4"/>
|
|
@@ -207,8 +255,8 @@
|
|
|
207
255
|
:class="{ disabled: !shouldShowProviderEdit(provider) }"
|
|
208
256
|
:disabled="!shouldShowProviderEdit(provider)"
|
|
209
257
|
@click="openEditModal(provider)"
|
|
210
|
-
:aria-label="
|
|
211
|
-
:title="shouldShowProviderEdit(provider) ? '
|
|
258
|
+
:aria-label="t('config.provider.edit.aria', { name: provider.name })"
|
|
259
|
+
:title="shouldShowProviderEdit(provider) ? t('common.edit') : t('common.notEditable')">
|
|
212
260
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
213
261
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
|
214
262
|
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
|
@@ -220,8 +268,8 @@
|
|
|
220
268
|
:class="{ disabled: !shouldShowProviderDelete(provider) }"
|
|
221
269
|
:disabled="!shouldShowProviderDelete(provider)"
|
|
222
270
|
@click="deleteProvider(provider.name)"
|
|
223
|
-
:aria-label="
|
|
224
|
-
:title="shouldShowProviderDelete(provider) ? '
|
|
271
|
+
:aria-label="t('config.provider.delete.aria', { name: provider.name })"
|
|
272
|
+
:title="shouldShowProviderDelete(provider) ? t('common.delete') : t('common.notDeletable')">
|
|
225
273
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
226
274
|
<path d="M3 6h18"/>
|
|
227
275
|
<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"/>
|
|
@@ -231,4 +279,5 @@
|
|
|
231
279
|
</div>
|
|
232
280
|
</div>
|
|
233
281
|
</div>
|
|
282
|
+
</template>
|
|
234
283
|
</div>
|