Skip to content

Commit 8ed55c6

Browse files
authored
fix(web): keep sidebar version and trash tab visible (#176)
* feat(web): keep sidebar version visible * fix(web): remove inert brand tab stop * fix(web): move sidebar ghost tab to nav end * docs: remove setup command from quick start * chore: bump version to 0.0.37
1 parent 7c5ddef commit 8ed55c6

10 files changed

Lines changed: 60 additions & 44 deletions

File tree

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ Requires [Node.js](https://nodejs.org/) (`brew install node` if not present).
8484

8585
```bash
8686
npm install -g codexmate
87-
codexmate setup
8887
codexmate run
8988
```
9089

README.zh.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ brew install codexmate
8484

8585
```bash
8686
npm install -g codexmate
87-
codexmate setup
8887
codexmate run
8988
```
9089

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codexmate",
3-
"version": "0.0.36",
3+
"version": "0.0.37",
44
"description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
55
"main": "cli.js",
66
"bin": {

tests/unit/config-tabs-ui.test.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ test('config template keeps expected config tabs in top and side navigation', ()
1818
const baseTheme = readProjectFile('web-ui/styles/base-theme.css');
1919
const controlsForms = readProjectFile('web-ui/styles/controls-forms.css');
2020
const taskOrchestrationStyles = readProjectFile('web-ui/styles/task-orchestration.css');
21+
const layoutShell = readProjectFile('web-ui/styles/layout-shell.css');
2122
const bundledStyles = readBundledWebUiCss();
2223
const sideRail = html.match(/<aside class="side-rail"[\s\S]*?<\/aside>/)?.[0] || '';
2324
const sideTabModes = [...html.matchAll(/id="side-tab-config-([a-z]+)"/g)]
@@ -120,6 +121,23 @@ test('config template keeps expected config tabs in top and side navigation', ()
120121
assert.match(styles, /\.task-action-row-right\s*\{[\s\S]*display:\s*flex;[\s\S]*flex-wrap:\s*wrap;/);
121122
assert.match(styles, /\.task-runtime-item-actions\s*\{[\s\S]*flex-direction:\s*row;[\s\S]*align-items:\s*center;/);
122123
}
124+
const sideGhostTab = sideRail.match(/<div id="side-tab-new"[\s\S]*?<\/div>\s*<\/div>/)?.[0] || '';
125+
assert.match(sideGhostTab, /class="side-item side-item-ghost"/);
126+
assert.match(sideGhostTab, /tabindex="-1"/);
127+
assert.match(sideGhostTab, /aria-hidden="true"/);
128+
assert.doesNotMatch(sideGhostTab, /data-main-tab=/);
129+
assert.doesNotMatch(sideGhostTab, /@click=/);
130+
assert.doesNotMatch(sideGhostTab, /@keydown/);
131+
assert.ok(html.indexOf('id="side-tab-trash"') < html.indexOf('id="side-tab-new"'), 'ghost side tab should remain after trash tab to reserve end scroll space');
132+
assert.match(html, /<div class="brand-kicker">Codex Mate<span v-if="appVersion" class="brand-version"> v\{\{ appVersion \}\}<\/span><\/div>/);
133+
assert.doesNotMatch(html, /class="brand-block" tabindex="0"/);
134+
assert.doesNotMatch(html, /appVersion && brandHovered/);
135+
assert.doesNotMatch(html, /brandHovered = true/);
136+
for (const styles of [layoutShell, bundledStyles]) {
137+
assert.match(styles, /\.side-item-ghost\s*\{[\s\S]*opacity:\s*0;[\s\S]*pointer-events:\s*none;[\s\S]*user-select:\s*none;/);
138+
assert.match(styles, /\.brand-kicker\s*\{[\s\S]*font-size:\s*15px;/);
139+
assert.match(styles, /\.brand-version\s*\{[\s\S]*font-size:\s*13px;/);
140+
}
123141
assert.match(html, /id="side-tab-market"/);
124142
assert.match(html, /id="tab-market"/);
125143
assert.match(html, /id="side-tab-docs"/);

tests/unit/web-ui-behavior-parity.test.mjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,6 @@ test('captured bundled app skeleton only exposes expected data key drift versus
325325
const missingCurrentKeys = headDataKeys.filter((key) => !currentDataKeys.includes(key)).sort();
326326
const allowedExtraCurrentKeys = parityAgainstHead ? [
327327
'appVersion',
328-
'brandHovered',
329328
'sessionListInitialBatchSize',
330329
'sessionListLoadStep',
331330
'sessionListVisibleCount',
@@ -355,7 +354,6 @@ test('captured bundled app skeleton only exposes expected data key drift versus
355354
'showEditProviderKey'
356355
] : [
357356
'appVersion',
358-
'brandHovered',
359357
'__mainTabSwitchState',
360358
'openclawAuthProfilesByProvider',
361359
'openclawPendingAuthProfileUpdates',
@@ -410,6 +408,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
410408
'promptTemplateVarDraftError',
411409
'promptTemplateVarDraftName',
412410
'showPromptTemplateVarModal',
411+
'brandHovered',
413412
];
414413
allowedExtraCurrentKeys.push(
415414
'lang',

web-ui/app.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ document.addEventListener('DOMContentLoaded', () => {
3131
const appOptions = {
3232
data() {
3333
return {
34-
brandHovered: false,
3534
lang: 'zh',
3635
appVersion: '',
3736
mainTab: 'dashboard',

web-ui/partials/index/layout-header.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@
118118

119119
<div :class="['app-shell', { standalone: sessionStandalone }]">
120120
<aside class="side-rail" v-if="!sessionStandalone">
121-
<div class="brand-block" tabindex="0" @mouseenter="brandHovered = true" @mouseleave="brandHovered = false" @focus="brandHovered = true" @blur="brandHovered = false">
121+
<div class="brand-block">
122122
<div class="brand-head">
123123
<img class="brand-logo" src="/res/logo-pack.webp" alt="Codex Mate logo">
124124
<div class="brand-copy">
125-
<div class="brand-kicker">Codex Mate<transition name="brand-version-fade"><span v-if="appVersion && brandHovered" class="brand-version"> v{{ appVersion }}</span></transition></div>
125+
<div class="brand-kicker">Codex Mate<span v-if="appVersion" class="brand-version"> v{{ appVersion }}</span></div>
126126
</div>
127127
</div>
128128
</div>
@@ -315,6 +315,10 @@
315315
</div>
316316
</button>
317317
</div>
318+
<div id="side-tab-new" class="side-item side-item-ghost" tabindex="-1" aria-hidden="true">
319+
<div class="side-item-title">New Tab</div>
320+
<div class="side-item-meta"><span>&nbsp;</span></div>
321+
</div>
318322
</div>
319323

320324
<div class="side-rail-lang" role="group" :aria-label="t('lang.label')">

web-ui/res/web-ui-render.precompiled.js

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
window.__CODEXMATE_WEB_UI_RENDER__ = (() => {
2-
const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, Transition: _Transition, withCtx: _withCtx, createVNode: _createVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, vShow: _vShow, withDirectives: _withDirectives, vModelSelect: _vModelSelect, vModelText: _vModelText, withKeys: _withKeys, withModifiers: _withModifiers, isMemoSame: _isMemoSame, withMemo: _withMemo, normalizeStyle: _normalizeStyle, vModelCheckbox: _vModelCheckbox, vModelDynamic: _vModelDynamic } = Vue
2+
const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock, createCommentVNode: _createCommentVNode, createTextVNode: _createTextVNode, Fragment: _Fragment, renderList: _renderList, vShow: _vShow, withDirectives: _withDirectives, vModelSelect: _vModelSelect, vModelText: _vModelText, withKeys: _withKeys, withModifiers: _withModifiers, isMemoSame: _isMemoSame, withMemo: _withMemo, normalizeStyle: _normalizeStyle, vModelCheckbox: _vModelCheckbox, vModelDynamic: _vModelDynamic } = Vue
33

44
return function render(_ctx, _cache) {
55
return (_openBlock(), _createElementBlock(_Fragment, null, [
@@ -165,14 +165,7 @@ return function render(_ctx, _cache) {
165165
key: 0,
166166
class: "side-rail"
167167
}, [
168-
_createElementVNode("div", {
169-
class: "brand-block",
170-
tabindex: "0",
171-
onMouseenter: $event => (_ctx.brandHovered = true),
172-
onMouseleave: $event => (_ctx.brandHovered = false),
173-
onFocus: $event => (_ctx.brandHovered = true),
174-
onBlur: $event => (_ctx.brandHovered = false)
175-
}, [
168+
_createElementVNode("div", { class: "brand-block" }, [
176169
_createElementVNode("div", { class: "brand-head" }, [
177170
_createElementVNode("img", {
178171
class: "brand-logo",
@@ -182,21 +175,16 @@ return function render(_ctx, _cache) {
182175
_createElementVNode("div", { class: "brand-copy" }, [
183176
_createElementVNode("div", { class: "brand-kicker" }, [
184177
_createTextVNode("Codex Mate"),
185-
_createVNode(_Transition, { name: "brand-version-fade" }, {
186-
default: _withCtx(() => [
187-
(_ctx.appVersion && _ctx.brandHovered)
188-
? (_openBlock(), _createElementBlock("span", {
189-
key: 0,
190-
class: "brand-version"
191-
}, " v" + _toDisplayString(_ctx.appVersion), 1 /* TEXT */))
192-
: _createCommentVNode("v-if", true)
193-
]),
194-
_: 1 /* STABLE */
195-
})
178+
(_ctx.appVersion)
179+
? (_openBlock(), _createElementBlock("span", {
180+
key: 0,
181+
class: "brand-version"
182+
}, " v" + _toDisplayString(_ctx.appVersion), 1 /* TEXT */))
183+
: _createCommentVNode("v-if", true)
196184
])
197185
])
198186
])
199-
], 40 /* PROPS, NEED_HYDRATION */, ["onMouseenter", "onMouseleave", "onFocus", "onBlur"]),
187+
]),
200188
_createElementVNode("div", { class: "side-rail-nav" }, [
201189
_createElementVNode("div", {
202190
class: "side-section",
@@ -437,7 +425,18 @@ return function render(_ctx, _cache) {
437425
: _createCommentVNode("v-if", true)
438426
])
439427
], 42 /* CLASS, PROPS, NEED_HYDRATION */, ["aria-current", "onPointerdown", "onClick"])
440-
], 8 /* PROPS */, ["aria-label"])
428+
], 8 /* PROPS */, ["aria-label"]),
429+
_createElementVNode("div", {
430+
id: "side-tab-new",
431+
class: "side-item side-item-ghost",
432+
tabindex: "-1",
433+
"aria-hidden": "true"
434+
}, [
435+
_createElementVNode("div", { class: "side-item-title" }, "New Tab"),
436+
_createElementVNode("div", { class: "side-item-meta" }, [
437+
_createElementVNode("span", null, " ")
438+
])
439+
])
441440
]),
442441
_createElementVNode("div", {
443442
class: "side-rail-lang",

web-ui/styles/layout-shell.css

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,14 @@ body::after {
415415
word-break: break-word;
416416
}
417417

418+
.side-item-ghost {
419+
opacity: 0;
420+
pointer-events: none;
421+
user-select: none;
422+
cursor: default;
423+
flex: 0 0 auto;
424+
}
425+
418426
@media (min-width: 721px) {
419427
body:not(.force-compact) #app > .top-tabs {
420428
display: none;
@@ -431,16 +439,6 @@ body::after {
431439
background: linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
432440
}
433441

434-
.brand-block:focus-visible {
435-
outline: 2px solid rgba(0, 122, 255, 0.5);
436-
outline-offset: -2px;
437-
border-radius: 4px;
438-
}
439-
440-
.brand-block:focus:not(:focus-visible) {
441-
outline: none;
442-
}
443-
444442
.brand-head {
445443
display: flex;
446444
align-items: center;
@@ -484,7 +482,7 @@ body::after {
484482
}
485483

486484
.brand-kicker {
487-
font-size: 13px;
485+
font-size: 15px;
488486
line-height: 1.2;
489487
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
490488
color: #1d1d1f;
@@ -502,10 +500,11 @@ body::after {
502500
}
503501

504502
.brand-version {
505-
font-size: 12px;
503+
font-size: 13px;
506504
font-weight: 400;
507505
color: #8e8e93;
508506
vertical-align: baseline;
507+
-webkit-text-fill-color: #8e8e93;
509508
}
510509

511510
.brand-version-fade-enter-active,

0 commit comments

Comments
 (0)