Skip to content

fix(web-ui): persist preferences and refine sidebar collapse#211

Merged
ymkiux merged 7 commits into
mainfrom
fix/task-tabs-disabled-selection-20260706
Jul 7, 2026
Merged

fix(web-ui): persist preferences and refine sidebar collapse#211
ymkiux merged 7 commits into
mainfrom
fix/task-tabs-disabled-selection-20260706

Conversation

@awsl233777

@awsl233777 awsl233777 commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Move Web UI preference persistence from browser localStorage to the existing backend preferences.json API.
  • Migrate and clear legacy localStorage keys for navigation, sidebar, session filters/trash, language, config selections, Claude/OpenClaw config, tool permissions, and related Web UI state.
  • Keep the task orchestration tab visible but disabled, and fall back to dashboard for ?tab=orchestration or restored orchestration state.
  • Add a persistent sidebar collapse toggle centered on the side rail edge.
  • Update the collapsed sidebar to show full tab labels instead of icons/abbreviations, with a 136px collapsed width so OpenClaw has more than 15px extra horizontal room.
  • Keep collapsed sidebar section labels, the top brand/version area, and the bottom language/settings entry visible so the rail still preserves navigation context.
  • Add bottom spacing to the scrollable side navigation so the fixed bottom language area cannot overlap the final tabs.
  • Bump the package version to 0.0.57 and push annotated git tag v0.0.57 to the current PR head commit.

Validation

  • Version scan: no remaining 0.0.56 / v0.0.56 strings outside git metadata; 0.0.57 appears in package.json and package-lock.json.
  • Git tag verification: v0.0.57 resolves to PR head commit 3cadaca2d53493ada9c51ef9929cd87c197c7edb.
  • git diff --check
  • npm run lint ([codexmate] Lint passed for 593 file(s).)
  • npm run test:unit (All 742 tests passed.)
  • npm run test:e2e
  • node -e "require('./tests/e2e/test-web-ui-session-tab.js')().then(()=>console.log('focused test passed')).catch((e)=>{console.error(e); process.exit(1);})" (focused test passed)
  • Manual browser/CDP check against isolated homes:
    • ?tab=orchestration falls back to dashboard.
    • Orchestration tab is disabled with aria-disabled="true", disabled, and tabindex="-1".
    • Migrated legacy localStorage keys are cleared.
    • Preferences are written to the isolated preferences.json.
    • Sidebar toggles between collapsed and 248px expanded states.
    • Collapse handle stays vertically centered on the side rail.
    • Collapsed sidebar width is 136px; OpenClaw text measured 58px, item width 119px, leaving 61px extra horizontal room.
    • Collapsed sidebar hides old icons and keeps all 15 full tab labels visible.
    • Collapsed sidebar keeps all 9 section labels visible.
    • Collapsed sidebar keeps the top brand area visible: logo, Codex Mate, and version/update copy are visible above Doctor.
    • Collapsed sidebar keeps the bottom language entry visible (语言:中文, measured 75px wide in the manual check).
    • Final tabs do not overlap the bottom language area; Trash has 66px clearance from the language footer in the collapsed-bottom check.
    • Masked Claude keys are sanitized and targetApi aliases are normalized before being persisted.

Screenshot evidence

Sent in the WeChat task thread:

  • /home/moltbot/clawd-wechat/tmp/codexmate-web-shots/orchestration-fallback-migrated-collapsed.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-web-shots/sidebar-expanded-after-toggle.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-web-shots/sidebar-collapsed-after-toggle.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-text-collapse-shots/collapsed-full-text.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-text-collapse-shots/expanded-after-text-collapse.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-collapse-section-language-shots/collapsed-section-titles-language-top.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-collapse-section-language-shots/collapsed-section-titles-language-bottom.png
  • /home/moltbot/clawd-wechat/tmp/codexmate-collapse-brand-shots/collapsed-brand-top.png

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR moves Web UI preferences to server-backed persistence with legacy migration, updates navigation to treat disabled tabs as non-selectable, and adds a collapsible sidebar with disabled orchestration rendering, icons, and matching styling/localization.

Changes

Web UI Preferences Persistence & Sidebar/Orchestration UI Refactor

Layer / File(s) Summary
Backend preference normalization
cli.js
Adds normalization helpers for sidebar state, session filters, and pinned maps; extends normalizeWebUiPreferences() with new fields and persists toolConfigPermissions.
Web UI preferences snapshot and migration
web-ui/modules/app.methods.web-ui-preferences.mjs, tests/unit/web-ui-preferences.test.mjs
Adds legacy preference migration, normalized snapshot building, merged persistence flushing, and tests for backend load, migration, and debounced writes.
Main-tab navigation gating and app wiring
web-ui/modules/app.methods.navigation.mjs, web-ui/app.js, web-ui/modules/app.methods.agents.mjs, tests/e2e/test-web-ui-session-tab.js, tests/unit/config-tabs-ui.test.mjs, tests/unit/web-ui-runtime-navigation-regression.test.mjs, tests/unit/web-ui-behavior-parity.test.mjs
Introduces disabled-tab resolution, cancels disabled-tab events, wires startup/watchers to the new persistence flow, and updates navigation tests.
Claude config and startup Claude persistence
web-ui/modules/app.methods.claude-config.mjs, web-ui/modules/app.methods.startup-claude.mjs, tests/unit/claude-settings-sync.test.mjs, tests/unit/startup-claude-star-prompt.test.mjs
Replaces localStorage writes for currentClaudeConfig, deleted-import tracking, provider-cache selection, and star-prompt state with preference-hook persistence.
Session, trash, tool-permission, openclaw, and language persistence
web-ui/modules/app.methods.session-actions.mjs, web-ui/modules/app.methods.session-browser.mjs, web-ui/modules/app.methods.session-trash.mjs, web-ui/modules/app.methods.tool-config-permissions.mjs, web-ui/modules/app.methods.openclaw-editing.mjs, web-ui/modules/config-template-confirm-pref.mjs, web-ui/modules/i18n.mjs, tests/unit/session-trash-state.test.mjs, tests/unit/session-browser-timeline-regression.test.mjs, tests/unit/startup-claude-star-prompt.test.mjs, tests/unit/web-ui-preferences.test.mjs, tests/unit/web-ui-behavior-parity.test.mjs, web-ui/modules/sessions-filters-url.mjs
Replaces localStorage reads and writes for session filters, pinned maps, retention days, tool permissions, openclaw configs, config-template confirmation, and language state with preference-hook persistence and updated test coverage.
Sidebar collapse and orchestration disabled-state UI
web-ui/partials/index/layout-header.html, web-ui/res/web-ui-render.precompiled.js, web-ui/styles/layout-shell.css, web-ui/styles/navigation-panels.css, web-ui/modules/i18n/locales/*, tests/unit/compact-layout-ui.test.mjs
Adds a collapsible sidebar with icon glyphs and collapse toggle, disables orchestration tab/nav entry, and adds matching CSS and locale strings.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AppJS as web-ui/app.js
  participant Prefs as app.methods.web-ui-preferences.mjs
  participant Storage as localStorage
  participant API as backend API

  AppJS->>Prefs: loadWebUiPreferences()
  Prefs->>API: get-web-ui-preferences
  Prefs->>Storage: collect legacy preference keys
  Prefs->>Prefs: merge and normalize snapshot
  Prefs->>AppJS: applyWebUiPreferences(merged)
  Prefs->>API: set-web-ui-preferences
  AppJS->>Prefs: persistWebUiPreferences(overrides)
  AppJS->>Prefs: flushWebUiPreferences()
  Prefs->>API: set-web-ui-preferences (pending overrides)
Loading
sequenceDiagram
  participant User
  participant NavHandlers as onMainTabPointerDown/onMainTabClick
  participant NavLogic as switchMainTab
  participant Prefs as persistNavState

  User->>NavHandlers: interact with main tab
  NavHandlers->>NavLogic: check tab selectability
  alt tab disabled
    NavHandlers-->>User: cancel event
  else tab selectable
    NavHandlers->>NavLogic: switchMainTab()
    NavLogic->>Prefs: persistNavState()
  end
Loading

Possibly related PRs

Suggested reviewers: ymkiux

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: moving preference persistence to the backend and refining the sidebar collapse behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/task-tabs-disabled-selection-20260706

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web-ui/modules/app.methods.session-browser.mjs (1)

248-287: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Wait for web UI preferences before restoring session filters. loadWebUiPreferences() is async and restoreSessionFilterCache() runs immediately after the fire-and-forget call, so startup can read the default sessionFilterSource/sessionPathFilter instead of the persisted values and skip the needed reload. Call restore after the preferences promise resolves or chain it from loadWebUiPreferences().

🤖 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/modules/app.methods.session-browser.mjs` around lines 248 - 287, The
session filter restore runs too early because `restoreSessionFilterCache()` is
invoked before `loadWebUiPreferences()` finishes, so persisted
`sessionFilterSource` and `sessionPathFilter` may not be available. Update the
startup flow in `app.methods.session-browser.mjs` so restoration happens only
after the async preferences load resolves, either by awaiting
`loadWebUiPreferences()` or chaining the restore call from it. Keep the reload
logic in `restoreSessionFilterCache()` and the `loadWebUiPreferences()` entry
point aligned so `loadSessions()` sees the restored state.
🧹 Nitpick comments (1)
web-ui/modules/app.methods.navigation.mjs (1)

436-439: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Redundant ternary — both branches are identical.

targetTab === 'config' ? this.configMode : this.configMode always evaluates to this.configMode, so the condition has no effect. Either simplify to configMode: this.configMode, or if a different value was intended for non-config targets (e.g., a cleared/default mode), fix the branch. Please confirm the intended behavior.

♻️ Simplify (if no distinct non-config value was intended)
             persistNavState(this, {
                 mainTab: targetTab,
-                configMode: targetTab === 'config' ? this.configMode : this.configMode
+                configMode: this.configMode
             });
🤖 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/modules/app.methods.navigation.mjs` around lines 436 - 439, The
persistNavState call in app.methods.navigation.mjs uses a redundant ternary for
configMode, since both branches return this.configMode and the condition has no
effect. Update the navigation logic around persistNavState to either pass
configMode: this.configMode directly, or if mainTab targetTab was meant to
change the value for non-config tabs, replace the identical branch with the
intended alternate mode. Use targetTab and configMode in this method to confirm
the behavior.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@web-ui/modules/app.methods.openclaw-editing.mjs`:
- Around line 362-367: saveOpenclawConfigs() is always returning success, which
prevents persistOpenclawConfig() from detecting a failed preference write and
rolling back the edit. Update saveOpenclawConfigs() in
app.methods.openclaw-editing.mjs to surface the real result of
persistWebUiPreferences instead of unconditionally returning true, and make
persistWebUiPreferences / set-web-ui-preferences propagate or expose write
failures so the caller can react. Keep the behavior aligned with
persistOpenclawConfig() by ensuring a failed OpenClaw config save returns a
failure signal rather than a success value.

---

Outside diff comments:
In `@web-ui/modules/app.methods.session-browser.mjs`:
- Around line 248-287: The session filter restore runs too early because
`restoreSessionFilterCache()` is invoked before `loadWebUiPreferences()`
finishes, so persisted `sessionFilterSource` and `sessionPathFilter` may not be
available. Update the startup flow in `app.methods.session-browser.mjs` so
restoration happens only after the async preferences load resolves, either by
awaiting `loadWebUiPreferences()` or chaining the restore call from it. Keep the
reload logic in `restoreSessionFilterCache()` and the `loadWebUiPreferences()`
entry point aligned so `loadSessions()` sees the restored state.

---

Nitpick comments:
In `@web-ui/modules/app.methods.navigation.mjs`:
- Around line 436-439: The persistNavState call in app.methods.navigation.mjs
uses a redundant ternary for configMode, since both branches return
this.configMode and the condition has no effect. Update the navigation logic
around persistNavState to either pass configMode: this.configMode directly, or
if mainTab targetTab was meant to change the value for non-config tabs, replace
the identical branch with the intended alternate mode. Use targetTab and
configMode in this method to confirm the behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5a82ec0-dbff-4829-958d-7820539ee382

📥 Commits

Reviewing files that changed from the base of the PR and between a088933 and 0908794.

📒 Files selected for processing (34)
  • cli.js
  • tests/e2e/test-web-ui-session-tab.js
  • tests/unit/claude-settings-sync.test.mjs
  • tests/unit/compact-layout-ui.test.mjs
  • tests/unit/config-tabs-ui.test.mjs
  • tests/unit/session-browser-timeline-regression.test.mjs
  • tests/unit/session-trash-state.test.mjs
  • tests/unit/startup-claude-star-prompt.test.mjs
  • tests/unit/web-ui-behavior-parity.test.mjs
  • tests/unit/web-ui-preferences.test.mjs
  • tests/unit/web-ui-runtime-navigation-regression.test.mjs
  • web-ui/app.js
  • web-ui/modules/app.methods.agents.mjs
  • web-ui/modules/app.methods.claude-config.mjs
  • web-ui/modules/app.methods.navigation.mjs
  • web-ui/modules/app.methods.openclaw-editing.mjs
  • web-ui/modules/app.methods.session-actions.mjs
  • web-ui/modules/app.methods.session-browser.mjs
  • web-ui/modules/app.methods.session-trash.mjs
  • web-ui/modules/app.methods.startup-claude.mjs
  • web-ui/modules/app.methods.tool-config-permissions.mjs
  • web-ui/modules/app.methods.web-ui-preferences.mjs
  • web-ui/modules/config-template-confirm-pref.mjs
  • web-ui/modules/i18n.mjs
  • web-ui/modules/i18n/locales/en.mjs
  • web-ui/modules/i18n/locales/ja.mjs
  • web-ui/modules/i18n/locales/vi.mjs
  • web-ui/modules/i18n/locales/zh-tw.mjs
  • web-ui/modules/i18n/locales/zh.mjs
  • web-ui/modules/sessions-filters-url.mjs
  • web-ui/partials/index/layout-header.html
  • web-ui/res/web-ui-render.precompiled.js
  • web-ui/styles/layout-shell.css
  • web-ui/styles/navigation-panels.css
💤 Files with no reviewable changes (2)
  • web-ui/modules/app.methods.session-trash.mjs
  • web-ui/modules/app.methods.tool-config-permissions.mjs
📜 Review details
🧰 Additional context used
🪛 Stylelint (17.14.0)
web-ui/styles/layout-shell.css

[error] 407-407: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🔇 Additional comments (49)
cli.js (2)

1003-1005: LGTM!

Also applies to: 1013-1060, 1074-1085, 1114-1116


1007-1011: 🎯 Functional Correctness

No missing session sources The supported values are all, codex, claude, gemini, and codebuddy; invalid values are normalized back before persistence, so this whitelist matches the UI.

			> Likely an incorrect or invalid review comment.
web-ui/modules/app.methods.web-ui-preferences.mjs (1)

1-273: LGTM!

Also applies to: 274-339, 341-446, 448-506

tests/unit/web-ui-preferences.test.mjs (1)

18-114: LGTM!

Also applies to: 126-177, 204-216

web-ui/app.js (1)

511-535: LGTM!

Also applies to: 547-583, 700-747

web-ui/modules/app.methods.agents.mjs (1)

356-368: LGTM!

tests/e2e/test-web-ui-session-tab.js (1)

91-94: LGTM!

Also applies to: 136-137, 171-218

tests/unit/config-tabs-ui.test.mjs (1)

74-92: LGTM!

tests/unit/web-ui-runtime-navigation-regression.test.mjs (1)

340-358: LGTM!

tests/unit/web-ui-behavior-parity.test.mjs (1)

487-493: LGTM!

Also applies to: 575-576, 793-801

web-ui/partials/index/layout-header.html (4)

125-135: 🩺 Stability & Availability

Verify collapse toggle behavior in compact/mobile layout.

The 64px collapse toggle is anchored to .side-rail's edge via absolute positioning. Confirm the toggle doesn't visually conflict or become inaccessible under body.force-compact/mobile layouts where the side rail's presentation changes.


167-167: LGTM!

Also applies to: 184-184, 202-202, 217-217, 232-232, 247-247, 266-266, 280-280, 297-297, 311-311, 347-347, 365-365, 383-383, 396-396


322-335: LGTM on the disabled-state wiring for the sidebar entry (aria-disabled, disabled, static class, rebuilding copy).


66-74: 🎯 Functional Correctness

Incorrect: orchestration is gated consistently. taskOrchestrationTabEnabled defaults to true, and both the top tab and sidebar orchestration entry use the same v-if, so there isn’t a top-tab-only visibility mismatch to fix.

			> Likely an incorrect or invalid review comment.
web-ui/res/web-ui-render.precompiled.js (1)

106-115: LGTM! This precompiled output correctly mirrors the disabled-tab and sidebar-collapse markup added to layout-header.html. (Note: the orchestration top-tab visibility gating issue flagged in that file's review will also need regenerating this precompiled output once fixed.)

Also applies to: 181-197, 240-565

web-ui/styles/layout-shell.css (2)

397-408: 📐 Maintainability & Code Quality | 💤 Low value

Stylelint: use lowercase currentcolor.

🔧 Suggested fix
 .side-item-icon {
     display: none;
     width: 28px;
     height: 28px;
     border-radius: 10px;
     align-items: center;
     justify-content: center;
     font-size: 15px;
     font-weight: 700;
     line-height: 1;
-    color: currentColor;
+    color: currentcolor;
 }

Source: Linters/SAST tools


22-99: LGTM on the collapsed-width variable, transitions, disabled-state visuals, and collapsed-mode overrides.

Also applies to: 428-438, 503-560

web-ui/modules/i18n/locales/zh.mjs (1)

150-151: LGTM!

Also applies to: 962-964

tests/unit/compact-layout-ui.test.mjs (1)

56-61: LGTM! Assertions correctly reflect the new --side-rail-width/--side-rail-collapsed-width CSS variable contract.

web-ui/styles/navigation-panels.css (1)

308-318: LGTM!

web-ui/modules/i18n/locales/en.mjs (1)

150-151: LGTM!

Also applies to: 963-965

web-ui/modules/i18n/locales/ja.mjs (1)

151-152: LGTM!

Also applies to: 952-954

web-ui/modules/i18n/locales/vi.mjs (1)

111-112: LGTM!

Also applies to: 1099-1101

web-ui/modules/i18n/locales/zh-tw.mjs (1)

150-151: LGTM!

Also applies to: 962-964

web-ui/modules/app.methods.claude-config.mjs (5)

91-97: LGTM!


99-126: Solid sanitization/normalization helper.

Clears masked keys and normalizes targetApi aliases correctly; changed flag logic is accurate.


155-182: LGTM!


263-274: LGTM!


497-499: LGTM!

Also applies to: 613-615

web-ui/modules/app.methods.startup-claude.mjs (4)

17-27: LGTM!


267-267: LGTM!


370-419: 🗄️ Data Integrity & Integration | 💤 Low value

Redundant intermediate persist with stale value in the imported-config branch.

In the imported-name branch, ensureClaudeConfigFromSettings internally calls saveClaudeConfigs() (persisting the old currentClaudeConfig) before this code updates this.currentClaudeConfig = importedName and persists again with the correct value. Functionally this self-corrects because the final persist call runs immediately after, but it produces an extra network round-trip and relies on the preferences layer applying writes in order/last-write-wins. Worth confirming that ordering guarantee holds in the persistence module.


602-608: LGTM!

tests/unit/claude-settings-sync.test.mjs (5)

626-675: LGTM!


696-717: LGTM!


1525-1581: 📐 Maintainability & Code Quality | 💤 Low value

Secret-leak assertion is vacuous.

assert.doesNotMatch(persistedJson, /sk-secret/) will always pass since none of the test fixtures contain that string — the test data's apiKey is already empty. This doesn't actually verify that a real (unmasked) secret is excluded from persisted preferences.


1582-1639: LGTM!


1668-1704: LGTM!

tests/unit/startup-claude-star-prompt.test.mjs (1)

10-55: LGTM!

web-ui/modules/i18n.mjs (2)

52-56: 🎯 Functional Correctness | ⚡ Quick win

Verify document language stays in sync after async preference load.

initI18n() now only normalizes/applies whatever this.lang currently holds instead of reading a persisted value itself; it relies entirely on an upstream layer setting this.lang from loaded Web UI preferences. If that upstream flow sets this.lang directly (e.g., via applyWebUiPreferences) without re-invoking applyDocumentLanguage/initI18n, the <html lang>/dir attributes could remain stale relative to the restored language after the preferences fetch resolves.


88-95: LGTM!

web-ui/modules/sessions-filters-url.mjs (1)

136-137: LGTM!

web-ui/modules/app.methods.session-actions.mjs (1)

2-2: LGTM!

Also applies to: 301-306, 309-314, 317-322, 330-335

web-ui/modules/app.methods.session-browser.mjs (3)

16-50: LGTM!


342-353: LGTM!

Also applies to: 771-777


289-311: 🗄️ Data Integrity & Integration

No change needed for onSessionSortChange
persistWebUiPreferences() deep-merges sessionFilters, so { sortMode: normalized } updates only that field and preserves source, pathFilter, query, roleFilter, and timePreset.

			> Likely an incorrect or invalid review comment.
tests/unit/session-trash-state.test.mjs (1)

2107-2115: LGTM!

Also applies to: 2396-2417

tests/unit/session-browser-timeline-regression.test.mjs (1)

98-126: LGTM!

web-ui/modules/config-template-confirm-pref.mjs (1)

12-22: 📐 Maintainability & Code Quality

Drop this concern No current call sites rely on the implicit localStorage fallback.

			> Likely an incorrect or invalid review comment.

Comment on lines 362 to 367
saveOpenclawConfigs() {
try {
localStorage.setItem('openclawConfigs', JSON.stringify(this.openclawConfigs));
return true;
} catch (_) {
this.showMessage('保存本地 OpenClaw 配置失败', 'error');
return false;
if (typeof this.persistWebUiPreferences === 'function') {
this.persistWebUiPreferences({ openclawConfigs: this.openclawConfigs });
}
return true;
},

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'saveOpenclawConfigs' -B2 -A6 web-ui

Repository: SakuraByteCore/codexmate

Length of output: 3356


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the persistence helper and related call sites.
sed -n '1,220p' web-ui/modules/app.methods.openclaw-persist.mjs
printf '\n---\n'
sed -n '1,220p' web-ui/modules/app.methods.web-ui-preferences.mjs
printf '\n---\n'
sed -n '220,380p' web-ui/modules/app.methods.openclaw-persist.mjs
printf '\n---\n'
sed -n '340,380p' web-ui/modules/app.methods.openclaw-editing.mjs

# Find any callers that depend on a boolean return.
printf '\n=== boolean-return callers ===\n'
rg -n 'saveOpenclawConfigs\(\)\s*===\s*false|saveOpenclawConfigs\(\)\s*===\s*true|if\s*\(\s*this\.saveOpenclawConfigs\(\)\s*\)' web-ui

Repository: SakuraByteCore/codexmate

Length of output: 27676


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the web UI preferences persistence implementation and its error handling.
rg -n 'persistWebUiPreferences|flushWebUiPreferences|set-web-ui-preferences|catch\(\(\)\s*=>\s*\{\}\)' web-ui/modules/app.methods.web-ui-preferences.mjs web-ui/modules -A6 -B6

printf '\n--- targeted slice ---\n'
sed -n '220,380p' web-ui/modules/app.methods.web-ui-preferences.mjs

Repository: SakuraByteCore/codexmate

Length of output: 50380


Restore a failure signal for OpenClaw saves saveOpenclawConfigs() now always returns true, so persistOpenclawConfig() can no longer roll back an edit when the preference write fails. The persistence path is fire-and-forget (set-web-ui-preferences is dispatched without awaiting and swallows rejections), so a save error leaves the UI showing success while the new OpenClaw config never reaches storage.

🤖 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/modules/app.methods.openclaw-editing.mjs` around lines 362 - 367,
saveOpenclawConfigs() is always returning success, which prevents
persistOpenclawConfig() from detecting a failed preference write and rolling
back the edit. Update saveOpenclawConfigs() in app.methods.openclaw-editing.mjs
to surface the real result of persistWebUiPreferences instead of unconditionally
returning true, and make persistWebUiPreferences / set-web-ui-preferences
propagate or expose write failures so the caller can react. Keep the behavior
aligned with persistOpenclawConfig() by ensuring a failed OpenClaw config save
returns a failure signal rather than a success value.

@awsl233777 awsl233777 changed the title fix(web-ui): persist preferences outside localStorage fix(web-ui): persist preferences and refine sidebar collapse Jul 6, 2026
@awsl233777

Copy link
Copy Markdown
Collaborator Author

Updated this PR with the collapsed-sidebar follow-up:

  • Replaced the collapsed sidebar icon/abbreviation treatment with full tab labels.
  • Increased the collapsed rail width to 136px.
  • Verified in the real Web UI that OpenClaw measures 58px, its collapsed item measures 119px, leaving 61px extra horizontal room.
  • Confirmed old sidebar item icons are hidden in collapsed mode and labels remain visible.

Validation run after the update:

  • git diff --check
  • npm run lint
  • npm run test:unit
  • npm run test:e2e
  • focused session-tab e2e command: focused test passed

Note: CodeRabbit was not checked or requested in this pass.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
web-ui/styles/layout-shell.css (1)

547-554: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing overflow containment for collapsed section title.

white-space: nowrap with no overflow/text-overflow fallback risks the section title spilling past the 136px collapsed rail for longer localized strings (ja/vi/zh-tw labels). The sibling .language-settings-link rule already uses overflow: hidden; text-overflow: ellipsis for the same scenario — worth mirroring here.

💄 Proposed fix
 .sidebar-collapsed .side-section-title {
     display: block;
     padding: 0 10px;
     margin: 4px 0 5px;
     text-align: left;
     white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
 }
🤖 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 547 - 554, The
.sidebar-collapsed .side-section-title rule needs overflow containment for long
localized labels. Update the side-section title styling in layout-shell.css to
mirror the existing .language-settings-link behavior by adding clipping and
ellipsis handling so nowrap text stays within the collapsed rail. Keep the fix
scoped to the .sidebar-collapsed .side-section-title selector and preserve the
current spacing/alignment while preventing spillover.
🤖 Prompt for all review comments with 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.

Nitpick comments:
In `@web-ui/styles/layout-shell.css`:
- Around line 547-554: The .sidebar-collapsed .side-section-title rule needs
overflow containment for long localized labels. Update the side-section title
styling in layout-shell.css to mirror the existing .language-settings-link
behavior by adding clipping and ellipsis handling so nowrap text stays within
the collapsed rail. Keep the fix scoped to the .sidebar-collapsed
.side-section-title selector and preserve the current spacing/alignment while
preventing spillover.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 30baa4a4-de37-4773-8e6f-30f7e6d899cb

📥 Commits

Reviewing files that changed from the base of the PR and between 052640f and 4f51d46.

📒 Files selected for processing (2)
  • tests/unit/compact-layout-ui.test.mjs
  • web-ui/styles/layout-shell.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/unit/compact-layout-ui.test.mjs
📜 Review details
🔇 Additional comments (1)
web-ui/styles/layout-shell.css (1)

519-526: LGTM!

Also applies to: 567-580

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Completed a stricter local review/fix pass for the collapsed sidebar:

  • Confirmed section labels remain visible in collapsed mode (9 / 9).
  • Confirmed full tab labels remain visible in collapsed mode (15 / 15).
  • Confirmed the bottom language entry remains visible as 语言:中文.
  • Found and fixed an edge risk where the final tab had only 2px clearance from the fixed language footer.
  • Added 64px bottom padding to the scrollable side navigation; the collapsed-bottom check now shows Trash has 66px clearance from the footer and no overlap.

Validation run after the fix:

  • git diff --check
  • npm run lint ([codexmate] Lint passed for 592 file(s).)
  • npm run test:unit (All 742 tests passed.)
  • npm run test:e2e
  • focused session-tab e2e command: focused test passed

Note: CodeRabbit was not checked or requested in this pass.

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Fixed the collapsed-sidebar top brand regression:

  • Restored the top brand area above Doctor in collapsed mode.
  • Confirmed the logo, Codex Mate, and version/update copy are visible in the real Web UI.
  • Confirmed the 概览 section label and Doctor 面板 tab remain visible below it.
  • Rechecked adjacent collapsed-sidebar behavior after the brand area became taller:
    • section labels visible: 9 / 9
    • tab labels visible: 15 / 15
    • bottom language entry visible as 语言:中文
    • Trash still has 66px clearance from the fixed footer, no overlap

Validation run after the fix:

  • git diff --check
  • npm run lint ([codexmate] Lint passed for 593 file(s).)
  • npm run test:unit (All 742 tests passed.)
  • npm run test:e2e
  • focused session-tab e2e command: focused test passed

Note: CodeRabbit was not checked or requested in this pass.

@awsl233777

Copy link
Copy Markdown
Collaborator Author

Updated the PR for v0.0.57:

  • Bumped package.json to 0.0.57.
  • Bumped package-lock.json root/package version entries to 0.0.57.
  • Verified there are no remaining 0.0.56 / v0.0.56 strings outside git metadata.
  • Pushed commit 3cadaca2d53493ada9c51ef9929cd87c197c7edb (chore(release): v0.0.57) to this PR branch.
  • Created and pushed annotated tag v0.0.57; it resolves to current PR head 3cadaca2d53493ada9c51ef9929cd87c197c7edb.

Validation run after the version bump:

  • git diff --check
  • npm run lint ([codexmate] Lint passed for 593 file(s).)
  • npm run test:unit (All 742 tests passed.)
  • npm run test:e2e

Note: CodeRabbit was not checked or requested in this pass.

@ymkiux ymkiux merged commit 042f455 into main Jul 7, 2026
12 checks passed
@ymkiux ymkiux deleted the fix/task-tabs-disabled-selection-20260706 branch July 7, 2026 05:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants