Skip to content

Commit d662385

Browse files
committed
style(web-ui): align OpenClaw config panel layout
1 parent 5769ed4 commit d662385

3 files changed

Lines changed: 212 additions & 63 deletions

File tree

web-ui/modules/i18n.dict.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,8 @@ const DICT = Object.freeze({
10521052

10531053
// OpenClaw config panel
10541054
'openclaw.applyHint': '写入 ~/.openclaw/openclaw.json,支持 JSON5。',
1055+
'openclaw.workspace.title': 'OpenClaw 工作区',
1056+
'openclaw.configs.hint': '选择常用配置,或进入编辑器维护完整 JSON5。',
10551057
'openclaw.agents.hint': '读写 Workspace 的 AGENTS.md,默认路径 ~/.openclaw/workspace/AGENTS.md。',
10561058
'openclaw.agents.open': '打开 AGENTS.md',
10571059
'openclaw.workspaceFile': '工作区文件',
@@ -3178,6 +3180,8 @@ const DICT = Object.freeze({
31783180

31793181
// OpenClaw config panel
31803182
'openclaw.applyHint': 'Writes to ~/.openclaw/openclaw.json (JSON5 supported).',
3183+
'openclaw.workspace.title': 'OpenClaw workspace',
3184+
'openclaw.configs.hint': 'Select a saved config, or open the editor to maintain full JSON5.',
31813185
'openclaw.agents.hint': 'Read/write Workspace AGENTS.md. Default: ~/.openclaw/workspace/AGENTS.md.',
31823186
'openclaw.agents.open': 'Open AGENTS.md',
31833187
'openclaw.workspaceFile': 'Workspace file',

web-ui/partials/index/panel-config-openclaw.html

Lines changed: 83 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,74 +10,94 @@
1010
<button type="button" :class="['segment', { active: configMode === 'claude' }]" @click="switchConfigMode('claude')">{{ t('tab.config.claude') }}</button>
1111
<button type="button" :class="['segment', { active: configMode === 'openclaw' }]" @click="switchConfigMode('openclaw')">{{ t('tab.config.openclaw') }}</button>
1212
</div>
13-
<div class="config-template-hint">
14-
{{ t('openclaw.applyHint') }}
15-
</div>
16-
17-
<div class="selector-section">
18-
<div class="selector-header">
19-
<span class="selector-title">AGENTS.md</span>
20-
</div>
21-
<div class="config-template-hint">
22-
{{ t('openclaw.agents.hint') }}
23-
</div>
24-
<button class="btn-tool" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
25-
{{ agentsLoading ? t('config.modelLoading') : t('openclaw.agents.open') }}
26-
</button>
27-
</div>
13+
<div class="openclaw-layout">
14+
<section class="settings-card settings-card--wide openclaw-overview-card" aria-labelledby="openclaw-workspace-title">
15+
<div class="settings-card-header settings-card-header-row">
16+
<div>
17+
<div id="openclaw-workspace-title" class="settings-card-title">{{ t('openclaw.workspace.title') }}</div>
18+
<div class="settings-card-meta">{{ t('openclaw.applyHint') }}</div>
19+
</div>
20+
</div>
21+
<div class="settings-card-body">
22+
<div class="openclaw-tools-grid">
23+
<div class="openclaw-tool-card">
24+
<div class="openclaw-tool-copy">
25+
<div class="openclaw-tool-title">AGENTS.md</div>
26+
<div class="openclaw-tool-meta">{{ t('openclaw.agents.hint') }}</div>
27+
</div>
28+
<div class="openclaw-tool-actions">
29+
<button class="btn-tool btn-tool-compact" @click="openOpenclawAgentsEditor" :disabled="loading || !!initError || agentsLoading">
30+
{{ agentsLoading ? t('config.modelLoading') : t('openclaw.agents.open') }}
31+
</button>
32+
</div>
33+
</div>
2834

29-
<div class="selector-section">
30-
<div class="selector-header">
31-
<label class="selector-title" for="openclaw-workspace-file">{{ t('openclaw.workspaceFile') }}</label>
32-
</div>
33-
<input
34-
id="openclaw-workspace-file"
35-
class="form-input"
36-
v-model="openclawWorkspaceFileName"
37-
:placeholder="t('openclaw.workspace.placeholder')">
38-
<div class="config-template-hint">
39-
{{ t('openclaw.workspace.hint') }}
40-
</div>
41-
<button class="btn-tool" @click="openOpenclawWorkspaceEditor" :disabled="loading || !!initError || agentsLoading">
42-
{{ agentsLoading ? t('config.modelLoading') : t('openclaw.workspace.open') }}
43-
</button>
44-
</div>
35+
<div class="openclaw-tool-card openclaw-tool-card--workspace">
36+
<div class="openclaw-tool-copy">
37+
<label class="openclaw-tool-title" for="openclaw-workspace-file">{{ t('openclaw.workspaceFile') }}</label>
38+
<div class="openclaw-tool-meta">{{ t('openclaw.workspace.hint') }}</div>
39+
</div>
40+
<div class="openclaw-workspace-row">
41+
<input
42+
id="openclaw-workspace-file"
43+
class="form-input"
44+
v-model="openclawWorkspaceFileName"
45+
:placeholder="t('openclaw.workspace.placeholder')">
46+
<button class="btn-tool btn-tool-compact" @click="openOpenclawWorkspaceEditor" :disabled="loading || !!initError || agentsLoading">
47+
{{ agentsLoading ? t('config.modelLoading') : t('openclaw.workspace.open') }}
48+
</button>
49+
</div>
50+
</div>
51+
</div>
52+
</div>
53+
</section>
4554

46-
<div class="card-list">
47-
<div v-for="(config, name) in openclawConfigs" :key="name"
48-
:class="['card', { active: currentOpenclawConfig === name }]"
49-
@click="applyOpenclawConfig(name)"
50-
@keydown.enter.self.prevent="applyOpenclawConfig(name)"
51-
@keydown.space.self.prevent="applyOpenclawConfig(name)"
52-
tabindex="0"
53-
role="button"
54-
:aria-current="currentOpenclawConfig === name ? 'true' : null">
55-
<div class="card-leading">
56-
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
57-
<div class="card-content">
58-
<div class="card-title">{{ name }}</div>
59-
<div class="card-subtitle">{{ openclawSubtitle(config) }}</div>
55+
<section class="settings-card settings-card--wide openclaw-configs-card" aria-labelledby="openclaw-configs-title">
56+
<div class="settings-card-header settings-card-header-row">
57+
<div>
58+
<div id="openclaw-configs-title" class="settings-card-title">{{ t('status.openclawConfig') }}</div>
59+
<div class="settings-card-meta">{{ t('openclaw.configs.hint') }}</div>
6060
</div>
6161
</div>
62-
<div class="card-trailing">
63-
<span :class="['pill', openclawHasContent(config) ? 'configured' : 'empty']">
64-
{{ openclawHasContent(config) ? t('openclaw.configured') : t('openclaw.notConfigured') }}
65-
</span>
66-
<div class="card-actions" @click.stop>
67-
<button class="card-action-btn" @click="openOpenclawEditModal(name)" :aria-label="t('openclaw.action.editAria', { name })" :title="t('openclaw.action.edit')">
68-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
69-
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
70-
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
71-
</svg>
72-
</button>
73-
<button v-if="name !== '默认配置'" class="card-action-btn delete" @click="deleteOpenclawConfig(name)" :aria-label="t('openclaw.action.deleteAria', { name })" :title="t('openclaw.action.delete')">
74-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
75-
<path d="M3 6h18"/>
76-
<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"/>
77-
</svg>
78-
</button>
62+
<div class="settings-card-body">
63+
<div class="card-list openclaw-card-list">
64+
<div v-for="(config, name) in openclawConfigs" :key="name"
65+
:class="['card', { active: currentOpenclawConfig === name }]"
66+
@click="applyOpenclawConfig(name)"
67+
@keydown.enter.self.prevent="applyOpenclawConfig(name)"
68+
@keydown.space.self.prevent="applyOpenclawConfig(name)"
69+
tabindex="0"
70+
role="button"
71+
:aria-current="currentOpenclawConfig === name ? 'true' : null">
72+
<div class="card-leading">
73+
<div class="card-icon">{{ name.charAt(0).toUpperCase() }}</div>
74+
<div class="card-content">
75+
<div class="card-title">{{ name }}</div>
76+
<div class="card-subtitle">{{ openclawSubtitle(config) }}</div>
77+
</div>
78+
</div>
79+
<div class="card-trailing">
80+
<span :class="['pill', openclawHasContent(config) ? 'configured' : 'empty']">
81+
{{ openclawHasContent(config) ? t('openclaw.configured') : t('openclaw.notConfigured') }}
82+
</span>
83+
<div class="card-actions" @click.stop>
84+
<button class="card-action-btn" @click="openOpenclawEditModal(name)" :aria-label="t('openclaw.action.editAria', { name })" :title="t('openclaw.action.edit')">
85+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
86+
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
87+
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
88+
</svg>
89+
</button>
90+
<button v-if="name !== '默认配置'" class="card-action-btn delete" @click="deleteOpenclawConfig(name)" :aria-label="t('openclaw.action.deleteAria', { name })" :title="t('openclaw.action.delete')">
91+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
92+
<path d="M3 6h18"/>
93+
<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"/>
94+
</svg>
95+
</button>
96+
</div>
97+
</div>
98+
</div>
7999
</div>
80100
</div>
81-
</div>
101+
</section>
82102
</div>
83103
</div>

web-ui/styles/openclaw-structured.css

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,128 @@
264264
word-break: break-all;
265265
}
266266

267+
268+
/* OpenClaw config tab: align with current settings/card system */
269+
.openclaw-layout {
270+
display: grid;
271+
gap: 12px;
272+
padding: 4px 0 0;
273+
}
274+
275+
.openclaw-overview-card,
276+
.openclaw-configs-card {
277+
margin: 0;
278+
}
279+
280+
.openclaw-tools-grid {
281+
display: grid;
282+
grid-template-columns: repeat(2, minmax(0, 1fr));
283+
gap: 12px;
284+
}
285+
286+
.openclaw-tool-card {
287+
min-width: 0;
288+
border-radius: var(--radius-lg);
289+
border: 1px solid var(--color-border-soft);
290+
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.88) 0%, rgba(248, 243, 238, 0.58) 100%);
291+
padding: 12px;
292+
display: grid;
293+
grid-template-columns: minmax(0, 1fr) auto;
294+
align-items: center;
295+
gap: 12px;
296+
box-shadow: var(--shadow-subtle);
297+
}
298+
299+
.openclaw-tool-card--workspace {
300+
grid-template-columns: minmax(0, 0.9fr) minmax(220px, 1.25fr);
301+
}
302+
303+
.openclaw-tool-copy {
304+
min-width: 0;
305+
display: flex;
306+
flex-direction: column;
307+
gap: 4px;
308+
}
309+
310+
.openclaw-tool-title {
311+
font-size: 13px;
312+
font-weight: var(--font-weight-title);
313+
color: var(--color-text-primary);
314+
letter-spacing: -0.01em;
315+
}
316+
317+
.openclaw-tool-meta {
318+
font-size: 11px;
319+
line-height: 1.35;
320+
color: var(--color-text-tertiary);
321+
}
322+
323+
.openclaw-tool-actions {
324+
display: flex;
325+
justify-content: flex-end;
326+
}
327+
328+
.openclaw-tool-actions .btn-tool,
329+
.openclaw-workspace-row .btn-tool {
330+
width: auto;
331+
white-space: nowrap;
332+
}
333+
334+
.openclaw-workspace-row {
335+
min-width: 0;
336+
display: grid;
337+
grid-template-columns: minmax(0, 1fr) auto;
338+
align-items: center;
339+
gap: 10px;
340+
}
341+
342+
.openclaw-workspace-row .form-input {
343+
min-height: 36px;
344+
}
345+
346+
.openclaw-card-list {
347+
margin-bottom: 0;
348+
}
349+
350+
.openclaw-card-list .card {
351+
background: rgba(255, 255, 255, 0.76);
352+
}
353+
354+
.openclaw-card-list .card-subtitle {
355+
white-space: normal;
356+
display: -webkit-box;
357+
-webkit-box-orient: vertical;
358+
-webkit-line-clamp: 2;
359+
overflow-wrap: anywhere;
360+
word-break: break-word;
361+
}
362+
363+
@media (max-width: 980px) {
364+
.openclaw-tools-grid,
365+
.openclaw-tool-card,
366+
.openclaw-tool-card--workspace,
367+
.openclaw-workspace-row {
368+
grid-template-columns: 1fr;
369+
}
370+
371+
.openclaw-tool-actions,
372+
.openclaw-workspace-row .btn-tool {
373+
justify-content: flex-start;
374+
width: 100%;
375+
}
376+
}
377+
378+
@media (max-width: 520px) {
379+
.openclaw-layout {
380+
gap: 10px;
381+
padding-top: 0;
382+
}
383+
384+
.openclaw-tool-card {
385+
padding: 10px;
386+
}
387+
388+
.openclaw-card-list .card {
389+
align-items: stretch;
390+
}
391+
}

0 commit comments

Comments
 (0)