Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -4879,27 +4879,29 @@ function listClaudeSessions(limit, options = {}) {
}
}

if (sessions.length === 0) {
const fallbackFiles = collectRecentJsonlFiles(claudeProjectsDir, {
returnCount: scanCount,
maxFilesScanned,
ignoreSubPath: `${path.sep}subagents${path.sep}`
// 补充扫描未索引的 .jsonl 文件(包括 sessions-index.json 中遗漏的会话)
const seenFilePaths = new Set(sessions.map((item) => item.filePath).filter(Boolean));
const fallbackFiles = collectRecentJsonlFiles(claudeProjectsDir, {
returnCount: scanCount,
maxFilesScanned,
ignoreSubPath: `${path.sep}subagents${path.sep}`
});
Comment on lines +4882 to +4888
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid unconditional fallback directory scan when targetCount is already met.

This now always traverses claudeProjectsDir, even if sessions.length has already reached targetCount. On large trees, that adds avoidable synchronous I/O and slows session list responses.

⚡ Suggested fix
-    // 补充扫描未索引的 .jsonl 文件(包括 sessions-index.json 中遗漏的会话)
-    const seenFilePaths = new Set(sessions.map((item) => item.filePath).filter(Boolean));
-    const fallbackFiles = collectRecentJsonlFiles(claudeProjectsDir, {
-        returnCount: scanCount,
-        maxFilesScanned,
-        ignoreSubPath: `${path.sep}subagents${path.sep}`
-    });
-    for (const filePath of fallbackFiles) {
+    // 补充扫描未索引的 .jsonl 文件(包括 sessions-index.json 中遗漏的会话)
+    if (sessions.length < targetCount) {
+        const seenFilePaths = new Set(sessions.map((item) => item.filePath).filter(Boolean));
+        const fallbackFiles = collectRecentJsonlFiles(claudeProjectsDir, {
+            returnCount: scanCount,
+            maxFilesScanned,
+            ignoreSubPath: `${path.sep}subagents${path.sep}`
+        });
+        for (const filePath of fallbackFiles) {
             if (seenFilePaths.has(filePath)) continue;
             const summary = parseClaudeSessionSummary(filePath, {
                 summaryReadBytes,
                 titleReadBytes
-        });
-        if (summary) {
-            sessions.push(attachSessionNativeStatus({
-                ...summary,
-                derived: isDerivedSessionFile(filePath)
-            }));
-            seenFilePaths.add(filePath);
-        }
+            });
+            if (summary) {
+                sessions.push(attachSessionNativeStatus({
+                    ...summary,
+                    derived: isDerivedSessionFile(filePath)
+                }));
+                seenFilePaths.add(filePath);
+            }
 
-        if (sessions.length >= targetCount) {
-            break;
+            if (sessions.length >= targetCount) {
+                break;
+            }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli.js` around lines 4882 - 4888, The code always calls
collectRecentJsonlFiles(claudeProjectsDir, { returnCount: scanCount,
maxFilesScanned, ignoreSubPath: ... }) even when sessions.length already meets
targetCount, causing unnecessary I/O; change the logic around seenFilePaths and
fallbackFiles so you only invoke collectRecentJsonlFiles when sessions.length <
targetCount (compute needed = targetCount - sessions.length and pass that as
returnCount instead of unconditional scanCount), and keep using seenFilePaths to
dedupe returned files; update any variable names used (scanCount/needed) so
collectRecentJsonlFiles is skipped when not needed.

for (const filePath of fallbackFiles) {
if (seenFilePaths.has(filePath)) continue;
const summary = parseClaudeSessionSummary(filePath, {
summaryReadBytes,
titleReadBytes
});
for (const filePath of fallbackFiles) {
const summary = parseClaudeSessionSummary(filePath, {
summaryReadBytes,
titleReadBytes
});
if (summary) {
sessions.push(attachSessionNativeStatus({
...summary,
derived: isDerivedSessionFile(filePath)
}));
}
if (summary) {
sessions.push(attachSessionNativeStatus({
...summary,
derived: isDerivedSessionFile(filePath)
}));
seenFilePaths.add(filePath);
}

if (sessions.length >= targetCount) {
break;
}
if (sessions.length >= targetCount) {
break;
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codexmate",
"version": "0.0.35",
"version": "0.0.36",
"description": "Codex/Claude Code/OpenClaw 配置、会话与任务编排 CLI + Web 工具",
"main": "cli.js",
"bin": {
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/web-ui-behavior-parity.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
const missingCurrentKeys = headDataKeys.filter((key) => !currentDataKeys.includes(key)).sort();
const allowedExtraCurrentKeys = parityAgainstHead ? [
'appVersion',
'brandHovered',
'sessionListInitialBatchSize',
'sessionListLoadStep',
'sessionListVisibleCount',
Expand Down Expand Up @@ -354,6 +355,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
'showEditProviderKey'
] : [
'appVersion',
'brandHovered',
'__mainTabSwitchState',
'openclawAuthProfilesByProvider',
'openclawPendingAuthProfileUpdates',
Expand Down
8 changes: 4 additions & 4 deletions tests/unit/web-ui-logic.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -957,8 +957,8 @@ test('activeSessionVisibleMessages falls back to the initial preview batch befor
});

test('formatSessionTimelineTimestamp normalizes ISO-like strings for timeline labels', () => {
assert.strictEqual(formatSessionTimelineTimestamp('2026-03-23T09:10:11.000Z'), '03-23 09:10:11');
assert.strictEqual(formatSessionTimelineTimestamp('2026-03-23 19:20:00'), '03-23 19:20:00');
assert.strictEqual(formatSessionTimelineTimestamp('2026-03-23T09:10:11.000Z'), '2026-03-23 09:10');
assert.strictEqual(formatSessionTimelineTimestamp('2026-03-23 19:20:00'), '2026-03-23 19:20');
assert.strictEqual(formatSessionTimelineTimestamp('not-a-time'), 'not-a-time');
assert.strictEqual(formatSessionTimelineTimestamp(''), '');
});
Expand All @@ -978,8 +978,8 @@ test('buildSessionTimelineNodes builds per-message node metadata', () => {
assert.strictEqual(nodes[0].key, 'user-0');
assert.strictEqual(nodes[0].role, 'user');
assert.strictEqual(nodes[0].roleShort, 'U');
assert.strictEqual(nodes[0].displayTime, '03-23 09:00:00');
assert.strictEqual(nodes[0].title, '#1 · User · 03-23 09:00:00');
assert.strictEqual(nodes[0].displayTime, '2026-03-23 09:00');
assert.strictEqual(nodes[0].title, '#1 · User · 2026-03-23 09:00');
assert.strictEqual(nodes[0].percent, 0);
assert.strictEqual(nodes[0].safePercent, 6);

Expand Down
1 change: 1 addition & 0 deletions web-ui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ document.addEventListener('DOMContentLoaded', () => {
const appOptions = {
data() {
return {
brandHovered: false,
lang: 'zh',
appVersion: '',
mainTab: 'dashboard',
Expand Down
7 changes: 2 additions & 5 deletions web-ui/logic.sessions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,9 @@ export function formatSessionTimelineTimestamp(timestamp) {
if (!value) return '';

const matched = value.match(/^(\d{4})-(\d{2})-(\d{2})[T\s](\d{2}):(\d{2})(?::(\d{2}))?/);
if (matched) {
const second = matched[6] || '00';
return `${matched[2]}-${matched[3]} ${matched[4]}:${matched[5]}:${second}`;
}
if (!matched) return value;

return value;
return `${matched[1]}-${matched[2]}-${matched[3]} ${matched[4]}:${matched[5]}`;
}

function normalizeUsageRange(range) {
Expand Down
5 changes: 5 additions & 0 deletions web-ui/modules/i18n.dict.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ const DICT = Object.freeze({
'sessions.loadingList': '会话加载中...',
'sessions.empty': '暂无可用会话记录',
'sessions.unknownTime': '未知时间',

'sessions.query.placeholder.enabled': '关键词检索(支持 Codex/Claude/Gemini/CodeBuddy,例:claude code)',
'sessions.query.placeholder.disabled': '当前来源暂不支持关键词检索',
'sessions.pin': '置顶',
Expand Down Expand Up @@ -1602,6 +1603,8 @@ const DICT = Object.freeze({
'sessions.loadingList': 'セッション一覧を読み込み中...',
'sessions.empty': 'セッションがありません',
'sessions.unknownTime': '不明な時間',


'sessions.query.placeholder.enabled': 'セッションを検索...',
'sessions.query.placeholder.disabled': '現在のソースでは検索は利用できません',
'sessions.pin': 'ピン留め',
Expand Down Expand Up @@ -2658,6 +2661,8 @@ const DICT = Object.freeze({
'sessions.loadingList': 'Loading sessions...',
'sessions.empty': 'No sessions found',
'sessions.unknownTime': 'unknown time',


'sessions.query.placeholder.enabled': 'Search keywords (Codex/Claude/Gemini/CodeBuddy, e.g. claude code)',
'sessions.query.placeholder.disabled': 'Keyword search is not available for this source',
'sessions.pin': 'Pin',
Expand Down
5 changes: 2 additions & 3 deletions web-ui/partials/index/layout-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,13 @@

<div :class="['app-shell', { standalone: sessionStandalone }]">
<aside class="side-rail" v-if="!sessionStandalone">
<div class="brand-block">
<div class="brand-block" tabindex="0" @mouseenter="brandHovered = true" @mouseleave="brandHovered = false" @focus="brandHovered = true" @blur="brandHovered = false">
<div class="brand-head">
<img class="brand-logo" src="/res/logo-pack.webp" alt="Codex Mate logo">
<div class="brand-copy">
<div class="brand-kicker">Codex Mate <span v-if="appVersion" class="brand-version">v{{ appVersion }}</span></div>
<div class="brand-kicker">Codex Mate<transition name="brand-version-fade"><span v-if="appVersion && brandHovered" class="brand-version"> v{{ appVersion }}</span></transition></div>
</div>
</div>
<div class="brand-subtitle">{{ t('brand.subtitle.localConfigSessionsWorkspace') }}</div>
</div>

<div class="side-rail-nav">
Expand Down
4 changes: 2 additions & 2 deletions web-ui/partials/index/panel-sessions.html
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
</div>
<div class="session-item-meta">
<span class="session-source" :data-source="session.source">{{ session.sourceLabel }}</span>
<span class="session-item-time">{{ session.updatedAt || t('sessions.unknownTime') }}</span>
<span class="session-item-time">{{ session.updatedAtLabel || session.updatedAt || t('sessions.unknownTime') }}</span>
<span v-if="getSessionHotLabel(session)" class="session-item-hot">{{ getSessionHotLabel(session) }}</span>
<span v-if="session.cwd" class="session-item-cwd session-item-sub">{{ session.cwd }}</span>
<div v-if="session.match && session.match.snippets && session.match.snippets.length" class="session-match-snippets">
Expand All @@ -212,7 +212,7 @@
<div class="session-preview-title">{{ activeSession.title || activeSession.sessionId }}</div>
<div class="session-preview-meta">
<span class="session-preview-meta-item session-source" :data-source="activeSession.source">{{ activeSession.sourceLabel }}</span>
<span class="session-preview-meta-item">{{ activeSession.updatedAt || t('sessions.unknownTime') }}</span>
<span class="session-preview-meta-item">{{ activeSession.updatedAtLabel || activeSession.updatedAt || t('sessions.unknownTime') }}</span>
</div>
<div class="session-preview-meta" v-if="activeSession.cwd">
<span class="session-preview-meta-item">{{ activeSession.cwd }}</span>
Expand Down
39 changes: 25 additions & 14 deletions web-ui/res/web-ui-render.precompiled.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
window.__CODEXMATE_WEB_UI_RENDER__ = (() => {
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
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

return function render(_ctx, _cache) {
return (_openBlock(), _createElementBlock(_Fragment, null, [
Expand Down Expand Up @@ -165,7 +165,14 @@ return function render(_ctx, _cache) {
key: 0,
class: "side-rail"
}, [
_createElementVNode("div", { class: "brand-block" }, [
_createElementVNode("div", {
class: "brand-block",
tabindex: "0",
onMouseenter: $event => (_ctx.brandHovered = true),
onMouseleave: $event => (_ctx.brandHovered = false),
onFocus: $event => (_ctx.brandHovered = true),
onBlur: $event => (_ctx.brandHovered = false)
}, [
_createElementVNode("div", { class: "brand-head" }, [
_createElementVNode("img", {
class: "brand-logo",
Expand All @@ -174,18 +181,22 @@ return function render(_ctx, _cache) {
}),
_createElementVNode("div", { class: "brand-copy" }, [
_createElementVNode("div", { class: "brand-kicker" }, [
_createTextVNode("Codex Mate "),
(_ctx.appVersion)
? (_openBlock(), _createElementBlock("span", {
key: 0,
class: "brand-version"
}, "v" + _toDisplayString(_ctx.appVersion), 1 /* TEXT */))
: _createCommentVNode("v-if", true)
_createTextVNode("Codex Mate"),
_createVNode(_Transition, { name: "brand-version-fade" }, {
default: _withCtx(() => [
(_ctx.appVersion && _ctx.brandHovered)
? (_openBlock(), _createElementBlock("span", {
key: 0,
class: "brand-version"
}, " v" + _toDisplayString(_ctx.appVersion), 1 /* TEXT */))
: _createCommentVNode("v-if", true)
]),
_: 1 /* STABLE */
})
])
])
]),
_createElementVNode("div", { class: "brand-subtitle" }, _toDisplayString(_ctx.t('brand.subtitle.localConfigSessionsWorkspace')), 1 /* TEXT */)
]),
])
], 40 /* PROPS, NEED_HYDRATION */, ["onMouseenter", "onMouseleave", "onFocus", "onBlur"]),
_createElementVNode("div", { class: "side-rail-nav" }, [
_createElementVNode("div", {
class: "side-section",
Expand Down Expand Up @@ -2447,7 +2458,7 @@ return function render(_ctx, _cache) {
class: "session-source",
"data-source": session.source
}, _toDisplayString(session.sourceLabel), 9 /* TEXT, PROPS */, ["data-source"]),
_createElementVNode("span", { class: "session-item-time" }, _toDisplayString(session.updatedAt || _ctx.t('sessions.unknownTime')), 1 /* TEXT */),
_createElementVNode("span", { class: "session-item-time" }, _toDisplayString(session.updatedAtLabel || session.updatedAt || _ctx.t('sessions.unknownTime')), 1 /* TEXT */),
(_ctx.getSessionHotLabel(session))
? (_openBlock(), _createElementBlock("span", {
key: 0,
Expand Down Expand Up @@ -2505,7 +2516,7 @@ return function render(_ctx, _cache) {
class: "session-preview-meta-item session-source",
"data-source": _ctx.activeSession.source
}, _toDisplayString(_ctx.activeSession.sourceLabel), 9 /* TEXT, PROPS */, ["data-source"]),
_createElementVNode("span", { class: "session-preview-meta-item" }, _toDisplayString(_ctx.activeSession.updatedAt || _ctx.t('sessions.unknownTime')), 1 /* TEXT */)
_createElementVNode("span", { class: "session-preview-meta-item" }, _toDisplayString(_ctx.activeSession.updatedAtLabel || _ctx.activeSession.updatedAt || _ctx.t('sessions.unknownTime')), 1 /* TEXT */)
]),
(_ctx.activeSession.cwd)
? (_openBlock(), _createElementBlock("div", {
Expand Down
9 changes: 7 additions & 2 deletions web-ui/session-helpers.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { buildSessionListParams } from './logic.mjs';
import { buildSessionListParams, formatSessionTimelineTimestamp } from './logic.mjs';

function clearSessionTimelineRefs(vm) {
if (typeof vm.clearSessionTimelineRefs === 'function') {
Expand Down Expand Up @@ -265,7 +265,12 @@ export async function loadSessions(api, options = {}) {
clearSessionTimelineRefs(this);
} else {
loadSucceeded = true;
this.sessionsList = Array.isArray(res.sessions) ? res.sessions : [];
const rawSessions = Array.isArray(res.sessions) ? res.sessions : [];
this.sessionsList = rawSessions.filter(s => s && typeof s === 'object');
for (const session of this.sessionsList) {
const rawUpdatedAt = typeof session.updatedAt === 'string' ? session.updatedAt : '';
session.updatedAtLabel = formatSessionTimelineTimestamp(rawUpdatedAt);
}
emitSessionLoadDebug(this, 'loadSessions:response', `sessions=${this.sessionsList.length}`);
if (typeof this.primeSessionListRender === 'function') {
this.primeSessionListRender();
Expand Down
93 changes: 66 additions & 27 deletions web-ui/styles/layout-shell.css
Original file line number Diff line number Diff line change
Expand Up @@ -425,58 +425,97 @@ body::after {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
margin-bottom: 0;
padding: 14px 10px 16px;
border-bottom: 1px solid rgba(137, 111, 94, 0.10);
padding: 16px 12px 20px;
border-bottom: 0.5px solid rgba(0, 0, 0, 0.06);
background: linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0) 100%);
}

.brand-block:focus-visible {
outline: 2px solid rgba(0, 122, 255, 0.5);
outline-offset: -2px;
border-radius: 4px;
}

.brand-block:focus:not(:focus-visible) {
outline: none;
}

.brand-head {
display: flex;
align-items: center;
gap: 12px;
gap: 10px;
}

.brand-logo {
width: 38px;
height: 38px;
border-radius: 13px;
width: 28px;
height: 28px;
border-radius: 6px;
Comment on lines +451 to +453
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check test file for logo dimension assertions
rg -n -A2 -B2 'brand-logo.*38px' tests/

Repository: SakuraByteCore/codexmate

Length of output: 738


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect the CSS around the referenced lines
echo "---- web-ui/styles/layout-shell.css: around 430-460 ----"
nl -ba web-ui/styles/layout-shell.css | sed -n '420,470p'

# 2) Find all .brand-logo blocks and any width/height assertions involving 38px/28px
echo "---- occurrences in layout-shell.css ----"
rg -n "\.brand-logo" -S web-ui/styles/layout-shell.css
echo "---- width/height values near brand-logo in layout-shell.css ----"
rg -n -S "brand-logo|width:\s*(38|28)px|height:\s*(38|28)px" web-ui/styles/layout-shell.css -n || true

# 3) Search all tests for explicit 38x38 expectations
echo "---- tests with brand-logo 38px expectations ----"
rg -n -A3 -B3 "brand-logo.*38px|\.brand-logo\\s*\\{[\\s\\S]*width:\\s*38px|height:\\s*38px" tests/ || true

# 4) Search all tests for 28px expectations
echo "---- tests with brand-logo 28px expectations ----"
rg -n -A3 -B3 "brand-logo.*28px|\.brand-logo\\s*\\{[\\s\\S]*width:\\s*28px|height:\\s*28px" tests/ || true

Repository: SakuraByteCore/codexmate

Length of output: 186


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect the CSS around the referenced lines
echo "---- web-ui/styles/layout-shell.css: around 430-470 ----"
nl -ba web-ui/styles/layout-shell.css | sed -n '420,470p'

# 2) Find all .brand-logo blocks and any width/height values near them
echo "---- occurrences in layout-shell.css ----"
rg -n "\.brand-logo" -S web-ui/styles/layout-shell.css

echo "---- width/height values in layout-shell.css (38/28) ----"
rg -n -S "width:\s*(38|28)px|height:\s*(38|28)px" web-ui/styles/layout-shell.css || true

# 3) Search all tests for explicit 38x38 expectations
echo "---- tests with brand-logo 38px expectations ----"
rg -n -A3 -B3 "brand-logo.*38px|\.brand-logo\\s*\\{[\\s\\S]*width:\\s*38px|height:\\s*38px" tests/ || true

# 4) Search all tests for 28x28 expectations
echo "---- tests with brand-logo 28px expectations ----"
rg -n -A3 -B3 "brand-logo.*28px|\.brand-logo\\s*\\{[\\s\\S]*width:\\s*28px|height:\\s*28px" tests/ || true

Repository: SakuraByteCore/codexmate

Length of output: 186


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Inspect CSS around the referenced lines (use cat -n since nl isn't available)
echo "---- web-ui/styles/layout-shell.css: around 430-470 ----"
cat -n web-ui/styles/layout-shell.css | sed -n '420,470p'

# 2) Find all .brand-logo blocks and any 38/28px width/height occurrences in that file
echo "---- occurrences in layout-shell.css ----"
rg -n "\\.brand-logo" -S web-ui/styles/layout-shell.css || true

echo "---- width/height values in layout-shell.css (38/28) ----"
rg -n -S "width:\\s*(38|28)px|height:\\s*(38|28)px" web-ui/styles/layout-shell.css || true

# 3) Search all tests for explicit 38x38 expectations for .brand-logo
echo "---- tests with brand-logo 38px expectations ----"
rg -n -A3 -B3 "brand-logo.*38px|\\.brand-logo\\s*\\{[\\s\\S]*width:\\s*38px|\\.brand-logo\\s*\\{[\\s\\S]*height:\\s*38px" tests/ || true

# 4) Search all tests for explicit 28x28 expectations for .brand-logo
echo "---- tests with brand-logo 28px expectations ----"
rg -n -A3 -B3 "brand-logo.*28px|\\.brand-logo\\s*\\{[\\s\\S]*width:\\s*28px|\\.brand-logo\\s*\\{[\\s\\S]*height:\\s*28px" tests/ || true

Repository: SakuraByteCore/codexmate

Length of output: 3079


Fix unit test for updated .brand-logo dimensions

web-ui/styles/layout-shell.css sets .brand-logo to width/height: 28px, but tests/unit/compact-layout-ui.test.mjs still asserts width: 38px; height: 38px for .brand-logo, which will fail in CI. Update the test expectation to 28px.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web-ui/styles/layout-shell.css` around lines 441 - 443, Update the unit test
assertion in compact-layout-ui.test.mjs that checks the .brand-logo dimensions:
change the expected width and height from "38px" to "28px" so it matches the
updated CSS (.brand-logo width/height: 28px). Locate the assertion referencing
".brand-logo" or the variables in the test (e.g., the selector or snapshot that
expects 38px) and update both width and height expectations to "28px".

object-fit: cover;
flex-shrink: 0;
box-shadow:
0 8px 20px rgba(92, 68, 52, 0.10),
0 0 0 1px rgba(200, 121, 99, 0.08);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.5);
transition: width 0.24s var(--ease-smooth), height 0.24s var(--ease-smooth), box-shadow 0.24s var(--ease-smooth);
position: relative;
}

.brand-logo::after {
content: "";
position: absolute;
inset: -4px;
background: radial-gradient(circle, rgba(0, 122, 255, 0.12), transparent 70%);
opacity: 0;
transition: opacity 0.3s var(--ease-smooth);
pointer-events: none;
}

.brand-block:hover .brand-logo {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

.brand-block:hover .brand-logo::after {
opacity: 1;
}

.brand-copy {
display: flex;
flex-direction: column;
gap: 4px;
gap: 0;
min-width: 0;
}

.brand-kicker {
font-size: 18px;
line-height: 1.15;
font-family: var(--font-family-display);
color: var(--color-text-primary);
letter-spacing: -0.02em;
font-weight: 700;
font-size: 13px;
line-height: 1.2;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, sans-serif;
color: #1d1d1f;
letter-spacing: -0.01em;
font-weight: 500;
}

.brand-version {
font-size: 11px;
font-weight: 500;
color: var(--color-text-muted);
vertical-align: middle;
margin-left: 4px;
@supports (-webkit-background-clip: text) or (background-clip: text) {
.brand-kicker {
background: linear-gradient(180deg, #1d1d1f 0%, #2d2d2f 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

.brand-subtitle {
.brand-version {
font-size: 12px;
line-height: 1.45;
color: var(--color-text-secondary);
max-width: 22ch;
font-weight: 400;
color: #8e8e93;
vertical-align: baseline;
}

.brand-version-fade-enter-active,
.brand-version-fade-leave-active {
transition: opacity 0.2s var(--ease-smooth);
}

.brand-version-fade-enter-from,
.brand-version-fade-leave-to {
opacity: 0;
}

.github-badge {
Expand Down
Loading