codexmate 0.0.38 → 0.0.40
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/cli/builtin-proxy.js +626 -207
- package/cli/config-bootstrap.js +6 -1
- package/cli/openai-bridge.js +541 -210
- package/cli.js +189 -4
- package/package.json +1 -1
- package/plugins/prompt-templates/computed.mjs +61 -3
- package/plugins/prompt-templates/manifest.mjs +3 -0
- package/web-ui/app.js +14 -3
- package/web-ui/modules/app.computed.main-tabs.mjs +39 -30
- package/web-ui/modules/app.methods.claude-config.mjs +111 -9
- package/web-ui/modules/app.methods.index.mjs +2 -0
- package/web-ui/modules/app.methods.openclaw-editing.mjs +48 -0
- package/web-ui/modules/app.methods.openclaw-persist.mjs +13 -7
- package/web-ui/modules/app.methods.providers.mjs +36 -10
- package/web-ui/modules/app.methods.runtime.mjs +76 -1
- package/web-ui/modules/app.methods.startup-claude.mjs +7 -0
- package/web-ui/modules/app.methods.tool-config-permissions.mjs +87 -0
- package/web-ui/modules/config-mode.computed.mjs +3 -3
- package/web-ui/modules/i18n/locales/en.mjs +1140 -0
- package/web-ui/modules/i18n/locales/ja.mjs +1130 -0
- package/web-ui/modules/i18n/locales/vi.mjs +239 -0
- package/web-ui/modules/i18n/locales/zh.mjs +1143 -0
- package/web-ui/modules/i18n.dict.mjs +9 -3195
- package/web-ui/modules/i18n.mjs +65 -16
- package/web-ui/partials/index/layout-header.html +16 -46
- package/web-ui/partials/index/modal-openclaw-config.html +135 -71
- package/web-ui/partials/index/modal-webhook.html +8 -8
- package/web-ui/partials/index/modals-basic.html +56 -16
- package/web-ui/partials/index/panel-config-claude.html +51 -21
- package/web-ui/partials/index/panel-config-codex.html +34 -5
- package/web-ui/partials/index/panel-config-openclaw.html +70 -64
- package/web-ui/partials/index/panel-dashboard.html +62 -77
- package/web-ui/partials/index/panel-settings.html +28 -7
- package/web-ui/partials/index/panel-trash.html +14 -14
- package/web-ui/res/web-ui-render.precompiled.js +1783 -1386
- package/web-ui/styles/controls-forms.css +99 -0
- package/web-ui/styles/dashboard.css +46 -14
- package/web-ui/styles/layout-shell.css +45 -0
- package/web-ui/styles/navigation-panels.css +3 -3
- package/web-ui/styles/openclaw-structured.css +383 -33
- package/web-ui/styles/responsive.css +68 -0
- package/web-ui/styles/sessions-usage.css +105 -9
- package/web-ui/styles/settings-panel.css +4 -0
package/web-ui/modules/i18n.mjs
CHANGED
|
@@ -2,11 +2,41 @@ import { DICT } from './i18n.dict.mjs';
|
|
|
2
2
|
|
|
3
3
|
const I18N_STORAGE_KEY = 'codexmateLang';
|
|
4
4
|
|
|
5
|
+
const LANGUAGE_META = Object.freeze([
|
|
6
|
+
Object.freeze({ code: 'zh', nativeName: '中文', englishName: 'Chinese', htmlLang: 'zh-CN', dir: 'ltr' }),
|
|
7
|
+
Object.freeze({ code: 'en', nativeName: 'English', englishName: 'English', htmlLang: 'en', dir: 'ltr' }),
|
|
8
|
+
Object.freeze({ code: 'ja', nativeName: '日本語', englishName: 'Japanese', htmlLang: 'ja', dir: 'ltr' }),
|
|
9
|
+
Object.freeze({ code: 'vi', nativeName: 'Tiếng Việt', englishName: 'Vietnamese', htmlLang: 'vi', dir: 'ltr' })
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
function getAvailableLanguages() {
|
|
13
|
+
return LANGUAGE_META.filter((item) => item && item.code && DICT[item.code]);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getDefaultLanguageMeta() {
|
|
17
|
+
const available = getAvailableLanguages();
|
|
18
|
+
return available[0] || LANGUAGE_META[0];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getLanguageMeta(code) {
|
|
22
|
+
const normalized = typeof code === 'string' ? code.trim().toLowerCase() : '';
|
|
23
|
+
return getAvailableLanguages().find((item) => item.code === normalized) || getDefaultLanguageMeta();
|
|
24
|
+
}
|
|
25
|
+
|
|
5
26
|
function normalizeLang(value) {
|
|
6
27
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return
|
|
28
|
+
const available = getAvailableLanguages();
|
|
29
|
+
const fallback = available[0] && available[0].code ? available[0].code : 'zh';
|
|
30
|
+
return available.some((item) => item.code === normalized) ? normalized : fallback;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function applyDocumentLanguage(next) {
|
|
34
|
+
try {
|
|
35
|
+
if (typeof document === 'undefined' || !document.documentElement) return;
|
|
36
|
+
const meta = getLanguageMeta(next);
|
|
37
|
+
document.documentElement.lang = meta.htmlLang || meta.code || 'zh-CN';
|
|
38
|
+
document.documentElement.dir = meta.dir || 'ltr';
|
|
39
|
+
} catch (_) {}
|
|
10
40
|
}
|
|
11
41
|
|
|
12
42
|
function interpolate(template, params) {
|
|
@@ -26,13 +56,38 @@ export function createI18nMethods() {
|
|
|
26
56
|
: '';
|
|
27
57
|
const next = normalizeLang(saved);
|
|
28
58
|
this.lang = next;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
59
|
+
applyDocumentLanguage(next);
|
|
60
|
+
},
|
|
61
|
+
languageOptions() {
|
|
62
|
+
return getAvailableLanguages();
|
|
63
|
+
},
|
|
64
|
+
currentLanguageLabel() {
|
|
65
|
+
return getLanguageMeta(this.lang).nativeName || '中文';
|
|
66
|
+
},
|
|
67
|
+
openLanguageSettings() {
|
|
68
|
+
if (typeof this.switchMainTab === 'function') {
|
|
69
|
+
this.switchMainTab('settings');
|
|
70
|
+
} else {
|
|
71
|
+
this.mainTab = 'settings';
|
|
72
|
+
if (typeof this.saveNavState === 'function') {
|
|
73
|
+
this.saveNavState();
|
|
34
74
|
}
|
|
35
|
-
}
|
|
75
|
+
}
|
|
76
|
+
this.settingsTab = 'general';
|
|
77
|
+
this.$nextTick(() => {
|
|
78
|
+
const target = typeof document !== 'undefined'
|
|
79
|
+
? document.getElementById('settings-language')
|
|
80
|
+
: null;
|
|
81
|
+
if (target && typeof target.scrollIntoView === 'function') {
|
|
82
|
+
target.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
83
|
+
}
|
|
84
|
+
const select = typeof document !== 'undefined'
|
|
85
|
+
? document.getElementById('settings-language-select')
|
|
86
|
+
: null;
|
|
87
|
+
if (select && typeof select.focus === 'function') {
|
|
88
|
+
select.focus();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
36
91
|
},
|
|
37
92
|
setLang(nextLang) {
|
|
38
93
|
const next = normalizeLang(nextLang);
|
|
@@ -42,13 +97,7 @@ export function createI18nMethods() {
|
|
|
42
97
|
localStorage.setItem(I18N_STORAGE_KEY, next);
|
|
43
98
|
}
|
|
44
99
|
} catch (_) {}
|
|
45
|
-
|
|
46
|
-
if (typeof document !== 'undefined' && document.documentElement) {
|
|
47
|
-
if (next === 'en') document.documentElement.lang = 'en';
|
|
48
|
-
else if (next === 'ja') document.documentElement.lang = 'ja';
|
|
49
|
-
else document.documentElement.lang = 'zh-CN';
|
|
50
|
-
}
|
|
51
|
-
} catch (_) {}
|
|
100
|
+
applyDocumentLanguage(next);
|
|
52
101
|
},
|
|
53
102
|
t(key, params = null) {
|
|
54
103
|
const lang = normalizeLang(this.lang);
|
|
@@ -93,27 +93,12 @@
|
|
|
93
93
|
@click="onMainTabClick('settings', $event)">{{ t('tab.settings') }}</button>
|
|
94
94
|
</div>
|
|
95
95
|
|
|
96
|
-
<div v-if="!sessionStandalone" class="lang-fab"
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
:class="{ active: (lang || 'zh') === 'zh' }"
|
|
103
|
-
@click="setLang('zh')">ZH</button>
|
|
104
|
-
<button
|
|
105
|
-
type="button"
|
|
106
|
-
class="lang-choice-btn"
|
|
107
|
-
:aria-pressed="(lang || 'zh') === 'en'"
|
|
108
|
-
:class="{ active: (lang || 'zh') === 'en' }"
|
|
109
|
-
@click="setLang('en')">EN</button>
|
|
110
|
-
<button
|
|
111
|
-
type="button"
|
|
112
|
-
class="lang-choice-btn"
|
|
113
|
-
:aria-pressed="(lang || 'zh') === 'ja'"
|
|
114
|
-
:class="{ active: (lang || 'zh') === 'ja' }"
|
|
115
|
-
@click="setLang('ja')">日本語</button>
|
|
116
|
-
</div>
|
|
96
|
+
<div v-if="!sessionStandalone" class="lang-fab">
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
class="language-settings-link"
|
|
100
|
+
:aria-label="t('settings.language.sideLabel', { language: currentLanguageLabel() })"
|
|
101
|
+
@click="openLanguageSettings">{{ t('settings.language.sideLabel', { language: currentLanguageLabel() }) }}</button>
|
|
117
102
|
</div>
|
|
118
103
|
|
|
119
104
|
<div :class="['app-shell', { standalone: sessionStandalone }]">
|
|
@@ -282,7 +267,7 @@
|
|
|
282
267
|
<div class="side-item-title">{{ t('side.plugins.tools') }}</div>
|
|
283
268
|
<div class="side-item-meta">
|
|
284
269
|
<span>{{ t('side.plugins.tools.meta') }}</span>
|
|
285
|
-
<span>{{ promptTemplatesList.length }}
|
|
270
|
+
<span>{{ t('side.plugins.templatesCount', { count: promptTemplatesList.length }) }}</span>
|
|
286
271
|
</div>
|
|
287
272
|
</button>
|
|
288
273
|
</div>
|
|
@@ -308,40 +293,25 @@
|
|
|
308
293
|
:class="['side-item', { active: isMainTabNavActive('trash') }]"
|
|
309
294
|
@pointerdown="onMainTabPointerDown('trash', $event)"
|
|
310
295
|
@click="onMainTabClick('trash', $event)">
|
|
311
|
-
<div class="side-item-title"
|
|
296
|
+
<div class="side-item-title">{{ t('settings.trash.title') }}</div>
|
|
312
297
|
<div class="side-item-meta">
|
|
313
|
-
<span
|
|
298
|
+
<span>{{ t('settings.trash.meta') }}</span>
|
|
314
299
|
<span v-if="sessionTrashCount > 0" class="side-item-badge">{{ sessionTrashCount }}</span>
|
|
315
300
|
</div>
|
|
316
301
|
</button>
|
|
317
302
|
</div>
|
|
318
303
|
<div id="side-tab-new" class="side-item side-item-ghost" tabindex="-1" aria-hidden="true">
|
|
319
|
-
<div class="side-item-title">
|
|
304
|
+
<div class="side-item-title">{{ t('side.newTab') }}</div>
|
|
320
305
|
<div class="side-item-meta"><span> </span></div>
|
|
321
306
|
</div>
|
|
322
307
|
</div>
|
|
323
308
|
|
|
324
|
-
<div class="side-rail-lang"
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
:class="{ active: (lang || 'zh') === 'zh' }"
|
|
331
|
-
@click="setLang('zh')">ZH</button>
|
|
332
|
-
<button
|
|
333
|
-
type="button"
|
|
334
|
-
class="lang-choice-btn"
|
|
335
|
-
:aria-pressed="(lang || 'zh') === 'en'"
|
|
336
|
-
:class="{ active: (lang || 'zh') === 'en' }"
|
|
337
|
-
@click="setLang('en')">EN</button>
|
|
338
|
-
<button
|
|
339
|
-
type="button"
|
|
340
|
-
class="lang-choice-btn"
|
|
341
|
-
:aria-pressed="(lang || 'zh') === 'ja'"
|
|
342
|
-
:class="{ active: (lang || 'zh') === 'ja' }"
|
|
343
|
-
@click="setLang('ja')">日本語</button>
|
|
344
|
-
</div>
|
|
309
|
+
<div class="side-rail-lang">
|
|
310
|
+
<button
|
|
311
|
+
type="button"
|
|
312
|
+
class="language-settings-link"
|
|
313
|
+
:aria-label="t('settings.language.sideLabel', { language: currentLanguageLabel() })"
|
|
314
|
+
@click="openLanguageSettings">{{ t('settings.language.sideLabel', { language: currentLanguageLabel() }) }}</button>
|
|
345
315
|
</div>
|
|
346
316
|
</aside>
|
|
347
317
|
<main class="main-panel">
|
|
@@ -33,86 +33,150 @@
|
|
|
33
33
|
<button class="btn-mini" @click="resetOpenclawQuick">{{ t('common.clear') }}</button>
|
|
34
34
|
</div>
|
|
35
35
|
</div>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<div
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
36
|
+
|
|
37
|
+
<!-- Accordion Stepper -->
|
|
38
|
+
<div class="quick-accordion">
|
|
39
|
+
<!-- Step 1: Provider -->
|
|
40
|
+
<div :class="['accordion-panel', { active: openclawAccordionStep === 1, completed: openclawAccordionStep > 1 }]">
|
|
41
|
+
<button class="accordion-trigger" @click="toggleAccordionStep(1)" type="button">
|
|
42
|
+
<span class="accordion-step-badge">
|
|
43
|
+
<template v-if="openclawAccordionStep > 1">✓</template>
|
|
44
|
+
<template v-else>1</template>
|
|
45
|
+
</span>
|
|
46
|
+
<span class="accordion-title">Provider</span>
|
|
47
|
+
<span class="accordion-status" v-if="openclawQuick.providerName">{{ openclawQuick.providerName }}</span>
|
|
48
|
+
<svg class="accordion-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
49
|
+
<path d="M6 9l6 6 6-6"/>
|
|
50
|
+
</svg>
|
|
51
|
+
</button>
|
|
52
|
+
<div class="accordion-content" v-show="openclawAccordionStep === 1">
|
|
53
|
+
<div class="form-group">
|
|
54
|
+
<label class="form-label">{{ t('field.providerName') }}</label>
|
|
55
|
+
<input v-model="openclawQuick.providerName" class="form-input" placeholder="例如: custom-myapi" @input="validateProviderName">
|
|
56
|
+
<div class="form-hint" :class="{ 'hint-error': !openclawValidation.providerName.valid }">
|
|
57
|
+
{{ openclawValidation.providerName.message || t('modal.openclaw.quick.providerHint') }}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="form-group">
|
|
61
|
+
<label class="form-label">{{ t('field.baseUrl') }}</label>
|
|
62
|
+
<input v-model="openclawQuick.baseUrl" class="form-input" placeholder="https://api.example.com/v1" :readonly="openclawQuick.baseUrlReadOnly">
|
|
63
|
+
<div v-if="openclawQuick.baseUrlDisplayKind === 'builtin-default'" class="form-hint">{{ t('modal.openclaw.quick.baseUrlHintDefault') }}</div>
|
|
64
|
+
<div v-else-if="openclawQuick.baseUrlReadOnly" class="form-hint">{{ t('modal.openclaw.quick.baseUrlHintReadonly') }}</div>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="form-group">
|
|
67
|
+
<label class="form-label">API Key</label>
|
|
68
|
+
<div class="list-row">
|
|
69
|
+
<input v-model="openclawQuick.apiKey" class="form-input" :readonly="openclawQuick.apiKeyReadOnly" :type="(openclawQuick.apiKeyReadOnly || openclawQuick.showKey) ? 'text' : 'password'" placeholder="sk-...">
|
|
70
|
+
<button v-if="!openclawQuick.apiKeyReadOnly" class="btn-mini" @click="toggleOpenclawQuickKey">
|
|
71
|
+
{{ openclawQuick.showKey ? t('common.hide') : t('common.show') }}
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
<div v-if="openclawQuick.apiKeyDisplayKind === 'auth-profile-value'" class="form-hint">{{ t('modal.openclaw.quick.apiKeyHintFromAuth') }}</div>
|
|
75
|
+
<div v-else-if="openclawQuick.apiKeyReadOnly" class="form-hint">{{ t('modal.openclaw.quick.apiKeyHintReadonly') }}</div>
|
|
76
|
+
<div v-else class="form-hint">{{ t('modal.openclaw.quick.apiKeyHintKeep') }}</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="form-group">
|
|
79
|
+
<label class="form-label">{{ t('field.apiType') }}</label>
|
|
80
|
+
<input v-model="openclawQuick.apiType" class="form-input" list="openclawApiTypeList" :placeholder="t('placeholder.apiTypeExample')">
|
|
81
|
+
<datalist id="openclawApiTypeList">
|
|
82
|
+
<option value="openai-responses"></option>
|
|
83
|
+
<option value="openai-chat"></option>
|
|
84
|
+
<option value="anthropic"></option>
|
|
85
|
+
<option value="custom"></option>
|
|
86
|
+
</datalist>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="accordion-actions">
|
|
89
|
+
<button class="btn btn-confirm btn-sm" @click="nextAccordionStep" :disabled="!openclawQuick.providerName">
|
|
90
|
+
下一步 →
|
|
61
91
|
</button>
|
|
62
92
|
</div>
|
|
63
|
-
<div v-if="openclawQuick.apiKeyDisplayKind === 'auth-profile-value'" class="form-hint">{{ t('modal.openclaw.quick.apiKeyHintFromAuth') }}</div>
|
|
64
|
-
<div v-else-if="openclawQuick.apiKeyReadOnly" class="form-hint">{{ t('modal.openclaw.quick.apiKeyHintReadonly') }}</div>
|
|
65
|
-
<div v-else class="form-hint">{{ t('modal.openclaw.quick.apiKeyHintKeep') }}</div>
|
|
66
|
-
</div>
|
|
67
|
-
<div class="form-group">
|
|
68
|
-
<label class="form-label">{{ t('field.apiType') }}</label>
|
|
69
|
-
<input v-model="openclawQuick.apiType" class="form-input" list="openclawApiTypeList" :placeholder="t('placeholder.apiTypeExample')">
|
|
70
|
-
<datalist id="openclawApiTypeList">
|
|
71
|
-
<option value="openai-responses"></option>
|
|
72
|
-
<option value="openai-chat"></option>
|
|
73
|
-
<option value="anthropic"></option>
|
|
74
|
-
<option value="custom"></option>
|
|
75
|
-
</datalist>
|
|
76
93
|
</div>
|
|
77
94
|
</div>
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
<!-- Step 2: Model -->
|
|
97
|
+
<div :class="['accordion-panel', { active: openclawAccordionStep === 2, completed: openclawAccordionStep > 2 }]">
|
|
98
|
+
<button class="accordion-trigger" @click="toggleAccordionStep(2)" type="button">
|
|
99
|
+
<span class="accordion-step-badge">
|
|
100
|
+
<template v-if="openclawAccordionStep > 2">✓</template>
|
|
101
|
+
<template v-else>2</template>
|
|
102
|
+
</span>
|
|
103
|
+
<span class="accordion-title">{{ t('modal.openclaw.quick.modelTitle') }}</span>
|
|
104
|
+
<span class="accordion-status" v-if="openclawQuick.modelId">{{ openclawQuick.modelName || openclawQuick.modelId }}</span>
|
|
105
|
+
<svg class="accordion-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
106
|
+
<path d="M6 9l6 6 6-6"/>
|
|
107
|
+
</svg>
|
|
108
|
+
</button>
|
|
109
|
+
<div class="accordion-content" v-show="openclawAccordionStep === 2">
|
|
110
|
+
<div class="form-group">
|
|
111
|
+
<label class="form-label">{{ t('field.modelId') }}</label>
|
|
112
|
+
<input v-model="openclawQuick.modelId" class="form-input" :placeholder="t('placeholder.modelIdExample')" @input="validateModelId">
|
|
113
|
+
<div class="form-hint" :class="{ 'hint-error': !openclawValidation.modelId.valid }">
|
|
114
|
+
{{ openclawValidation.modelId.message || '必填,例如: gpt-4' }}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="form-group">
|
|
118
|
+
<label class="form-label">{{ t('field.displayName') }}</label>
|
|
119
|
+
<input v-model="openclawQuick.modelName" class="form-input" :placeholder="t('placeholder.modelNameOptional')">
|
|
120
|
+
<div class="form-hint">可选,用于显示</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="form-group">
|
|
123
|
+
<label class="form-label">{{ t('field.contextAndMaxOutput') }}</label>
|
|
124
|
+
<div class="list-row">
|
|
125
|
+
<input v-model="openclawQuick.contextWindow" class="form-input" :placeholder="t('field.contextWindow')">
|
|
126
|
+
<input v-model="openclawQuick.maxTokens" class="form-input" :placeholder="t('field.maxOutput')">
|
|
127
|
+
</div>
|
|
128
|
+
<div class="form-hint">{{ t('hint.emptyNoChange') }}</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="accordion-actions">
|
|
131
|
+
<button class="btn btn-cancel btn-sm" @click="prevAccordionStep">
|
|
132
|
+
← 上一步
|
|
133
|
+
</button>
|
|
134
|
+
<button class="btn btn-confirm btn-sm" @click="nextAccordionStep" :disabled="!openclawQuick.modelId">
|
|
135
|
+
下一步 →
|
|
136
|
+
</button>
|
|
94
137
|
</div>
|
|
95
|
-
<div class="form-hint">{{ t('hint.emptyNoChange') }}</div>
|
|
96
138
|
</div>
|
|
97
139
|
</div>
|
|
98
140
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
141
|
+
<!-- Step 3: Options -->
|
|
142
|
+
<div :class="['accordion-panel', { active: openclawAccordionStep === 3, completed: openclawAccordionStep > 3 }]">
|
|
143
|
+
<button class="accordion-trigger" @click="toggleAccordionStep(3)" type="button">
|
|
144
|
+
<span class="accordion-step-badge">
|
|
145
|
+
<template v-if="openclawAccordionStep > 3">✓</template>
|
|
146
|
+
<template v-else>3</template>
|
|
147
|
+
</span>
|
|
148
|
+
<span class="accordion-title">{{ t('modal.openclaw.quick.optionsTitle') }}</span>
|
|
149
|
+
<span class="accordion-status">高级选项</span>
|
|
150
|
+
<svg class="accordion-chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
151
|
+
<path d="M6 9l6 6 6-6"/>
|
|
152
|
+
</svg>
|
|
153
|
+
</button>
|
|
154
|
+
<div class="accordion-content" v-show="openclawAccordionStep === 3">
|
|
155
|
+
<label class="quick-option">
|
|
156
|
+
<input type="checkbox" v-model="openclawQuick.setPrimary">
|
|
157
|
+
{{ t('modal.openclaw.quick.setPrimary') }}
|
|
158
|
+
</label>
|
|
159
|
+
<label class="quick-option">
|
|
160
|
+
<input type="checkbox" v-model="openclawQuick.overrideProvider">
|
|
161
|
+
{{ t('modal.openclaw.quick.overrideProvider') }}
|
|
162
|
+
</label>
|
|
163
|
+
<label class="quick-option">
|
|
164
|
+
<input type="checkbox" v-model="openclawQuick.overrideModels">
|
|
165
|
+
{{ t('modal.openclaw.quick.overrideModels') }}
|
|
166
|
+
</label>
|
|
167
|
+
<div class="form-hint">{{ t('modal.openclaw.quick.optionsHint') }}</div>
|
|
168
|
+
<div class="accordion-actions">
|
|
169
|
+
<button class="btn btn-cancel btn-sm" @click="prevAccordionStep">
|
|
170
|
+
← 上一步
|
|
171
|
+
</button>
|
|
172
|
+
<button class="btn btn-confirm btn-sm" @click="finishAccordionStep">
|
|
173
|
+
完成 ✓
|
|
174
|
+
</button>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
114
177
|
</div>
|
|
115
178
|
</div>
|
|
179
|
+
|
|
116
180
|
<div class="btn-group">
|
|
117
181
|
<button class="btn btn-confirm" @click="applyOpenclawQuickToText">{{ t('modal.openclaw.quick.writeToEditor') }}</button>
|
|
118
182
|
</div>
|
|
@@ -262,14 +326,14 @@
|
|
|
262
326
|
:readonly="openclawSaving || openclawApplying"
|
|
263
327
|
placeholder="在这里编辑 OpenClaw 配置(JSON5)"></textarea>
|
|
264
328
|
<div class="template-editor-warning">
|
|
265
|
-
<span v-if="openclawEditing.lockName && openclawEditing.name
|
|
329
|
+
<span v-if="openclawEditing.lockName && isDefaultOpenclawConfig(openclawEditing.name)">默认配置始终映射当前 openclaw.json,请直接使用“保存并应用”。</span>
|
|
266
330
|
<span v-else>保存仅写入本地配置库。点击“保存并应用”后会写入 openclaw.json。</span>
|
|
267
331
|
</div>
|
|
268
332
|
</div>
|
|
269
333
|
|
|
270
334
|
<div class="btn-group">
|
|
271
335
|
<button class="btn btn-cancel" @click="closeOpenclawConfigModal" :disabled="openclawSaving || openclawApplying">取消</button>
|
|
272
|
-
<button class="btn btn-confirm" @click="saveOpenclawConfig" :disabled="openclawSaving || openclawApplying || (openclawEditing.lockName && openclawEditing.name
|
|
336
|
+
<button class="btn btn-confirm" @click="saveOpenclawConfig" :disabled="openclawSaving || openclawApplying || (openclawEditing.lockName && isDefaultOpenclawConfig(openclawEditing.name))">
|
|
273
337
|
{{ openclawSaving ? '保存中...' : '保存' }}
|
|
274
338
|
</button>
|
|
275
339
|
<button class="btn btn-confirm secondary" @click="saveAndApplyOpenclawConfig" :disabled="openclawSaving || openclawApplying">
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
<!-- Webhook
|
|
1
|
+
<!-- Webhook settings modal -->
|
|
2
2
|
<div v-if="showWebhookModal" class="modal-overlay" @click.self="closeWebhookModal">
|
|
3
3
|
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="webhook-modal-title">
|
|
4
|
-
<div class="modal-title" id="webhook-modal-title">
|
|
4
|
+
<div class="modal-title" id="webhook-modal-title">{{ t('settings.webhook.modalTitle') }}</div>
|
|
5
5
|
|
|
6
6
|
<div class="form-group">
|
|
7
|
-
<label class="form-label"
|
|
7
|
+
<label class="form-label">{{ t('settings.webhook.enabledLabel') }}</label>
|
|
8
8
|
<label class="settings-toggle">
|
|
9
9
|
<input type="checkbox" v-model="webhookConfig.enabled">
|
|
10
|
-
<span
|
|
10
|
+
<span>{{ t('settings.webhook.enableToggle') }}</span>
|
|
11
11
|
</label>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
<div class="form-group">
|
|
15
|
-
<label class="form-label">
|
|
15
|
+
<label class="form-label">{{ t('settings.webhook.urlLabel') }}</label>
|
|
16
16
|
<input
|
|
17
17
|
v-model="webhookConfig.url"
|
|
18
18
|
class="form-input"
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
25
|
<div class="form-group">
|
|
26
|
-
<label class="form-label"
|
|
26
|
+
<label class="form-label">{{ t('settings.webhook.eventsLabel') }}</label>
|
|
27
27
|
<div class="webhook-events-checkbox-list">
|
|
28
28
|
<label v-for="ev in webhookEventOptions" :key="ev" class="webhook-event-checkbox-item">
|
|
29
29
|
<input type="checkbox" :checked="webhookConfig.events.includes(ev)" @change="toggleWebhookEvent(ev)">
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
</div>
|
|
34
34
|
|
|
35
35
|
<div class="btn-group">
|
|
36
|
-
<button class="btn btn-cancel" @click="closeWebhookModal"
|
|
36
|
+
<button class="btn btn-cancel" @click="closeWebhookModal">{{ t('common.cancel') }}</button>
|
|
37
37
|
<button class="btn btn-confirm" @click="saveWebhookSettings" :disabled="webhookSaving">
|
|
38
|
-
{{ webhookSaving ? '
|
|
38
|
+
{{ webhookSaving ? t('common.saving') : t('common.save') }}
|
|
39
39
|
</button>
|
|
40
40
|
</div>
|
|
41
41
|
</div>
|