feat(sidebar): group + customize sidebar items, tabbed Settings#216
Conversation
|
The branch looks like it was forked from git fetch origin
git rebase origin/main
# resolve conflicts (likely in src/frontend/app.js, index.html, styles.css since both PRs touch the sidebar / settings area)
git push --force-with-leaseOnce the diff drops to just the sidebar work ( |
Sidebar is now organized into 3 collapsible sections — Workspace, Agents, Tools — with Install agents as a default-collapsed sub-section. Each entry can be hidden via Settings → Sidebar. Section state and per-item visibility persist in localStorage['codedash-sidebar-config'] (schema v:1). Settings page itself is restructured into 4 sub-tabs: Appearance, Sidebar, Sessions, Integrations. Accessibility: - WAI-ARIA tablist: arrow-key navigation, aria-controls/aria-labelledby. - aria-expanded on section headers; aria-live status region for toggles. - Settings is always visible (UI disabled + model-layer refusal); never lockable from localStorage. - prefers-reduced-motion suppresses chevron transition. - rel="noopener noreferrer" on the external author link. Security: - localStorage payload capped at 64 KB. - __proto__ / constructor / prototype keys explicitly blocked at sanitize. - Config holds only booleans; keys from a hardcoded allow-list; all DOM strings flow through escHtml. No fetch / no network calls added. Tests: 34 node:test cases for pure helpers (parse, serialize, mutators, storage adapters, edge cases) — all green. Spec / BDD / plan / smoke artifacts under docs/design/ specs/ tasks/. Deferred: - tech-debt: cache .sidebar-item NodeList in applySidebarConfig — premature optimization at ~30 elements; profile-driven if it ever shows up. - out-of-scope: count badge on collapsed "Install agents" — cosmetic polish, will land in a follow-up UI PR.
7f5232e to
87386eb
Compare
|
Rebased onto current |
vakovalskii
left a comment
There was a problem hiding this comment.
LGTM ✅ Rebase is clean — diff against current main is now only the sidebar work, no collateral deletions.
Verified:
- CI green 6/6 (and now actually runs tests thanks to #217)
node --test test/sidebar-config.test.js→ 34/34 pass- Full suite: 113 pass / 0 fail / 2 skipped (cross-platform skips)
Code quality:
sidebar-config.jspure UMD module with allow-list forKNOWN_ITEM_KEYS, 64KB localStorage cap, proto-pollution guard (__proto__/constructor/prototypeblocked), booleans only in config- WAI-ARIA tablist with arrow-key nav + roving tabindex,
aria-expanded,aria-livefor toggle announcements - Settings entry has three-layer lockout (disabled checkbox + isItemHidden short-circuit + setItemHidden refusal)
- Pre-paint script in index.html avoids FOUC on persisted collapsed state
- SDD/BDD/plan artefacts under
docs/design/+specs/+tasks/
Thanks for the rebase, @NovakPAai! 🙏
Summary
.ap-tabsvisual pattern as the Add Project modal.localStorage['codedash-sidebar-config']v:1 +codedash-settings-tab). No server changes, no new endpoints, no network calls.What's new
<div class="sidebar-group" data-section>blocks with<button>headers; nested install-agents sub-groupsrc/frontend/sidebar-config.js(new)parseSidebarConfig/serializeSidebarConfig/isItemHidden/isSectionCollapsed/setItemHidden/setSectionCollapsed/loadFromStorage/saveToStorage/clearStorage/resetConfig+KNOWN_ITEM_KEYSallow-listsrc/frontend/app.jsapplySidebarConfig, section-header click handler,toggleSidebarItem, two-stageonResetSidebarClick,renderSidebarSettingsGroup,onSettingsTabKey(keyboard nav),_announceSidebar(aria-live), tabbedrenderSettingssrc/frontend/styles.css.sidebar-group,.sidebar-section-header(32px min height), chevron rotation,.sidebar-subgroupindent,.settings-sidebar-*rows,.settings-tabs,.sr-onlyindex.htmlcollapsedclass before sidebar markup paints — prevents flash of expanded statetest/sidebar-config.test.js(new)node:testcases — all greenAccessibility
aria-controls,aria-labelledby, rovingtabindex.aria-expandedon section headers;aria-live="polite"status region announces show/hide toggles.disabled,isItemHiddenshort-circuits,setItemHiddenrefuses to write. Three-layer lockout protection.prefers-reduced-motionsuppresses chevron transition.--text-mutedto--text-secondary(WCAG 1.4.3 AA).Security
localStoragepayload capped at 64 KB (synchronous parse on init — DoS guard).__proto__/constructor/prototypekeys explicitly blocked insanitizeMap.<a target="_blank">getrel="noopener noreferrer".Spec-driven artifacts
docs/design/sidebar-customization.md— SDD with full UX/a11y sectionspecs/sidebar-customization.feature— 13 BDD scenarios (happy / empty / loading / error / keyboard / edge)tasks/2026-05-15-sidebar-customization/plan.md— implementation plan + risk register (all 10 risks closed)tasks/2026-05-15-sidebar-customization/smoke-report.md— automated smoke verdictDeferred (tracked in commit message)
.sidebar-itemNodeList inapplySidebarConfig— premature optimization for ~30 elementsTest plan
node --test test/sidebar-config.test.js— 34/34 passlocalStorage['codedash-sidebar-config'] = '{garbage'→ reload → default layout, no error UI#leaderboardwhile Leaderboard hidden → view renders, sidebar link stays hidden