Skip to content

feat(sidebar): group + customize sidebar items, tabbed Settings#216

Merged
vakovalskii merged 1 commit into
mainfrom
feat/sidebar-customization
May 18, 2026
Merged

feat(sidebar): group + customize sidebar items, tabbed Settings#216
vakovalskii merged 1 commit into
mainfrom
feat/sidebar-customization

Conversation

@NovakPAai
Copy link
Copy Markdown
Collaborator

Summary

  • Sidebar reorganized into 3 collapsible sections — Workspace, Agents, Tools — with Install agents as a default-collapsed sub-section inside Tools. Each entry can be individually hidden via the new Settings → Sidebar tab.
  • Settings page itself restructured into 4 sub-tabs (Appearance / Sidebar / Sessions / Integrations) using the same .ap-tabs visual pattern as the Add Project modal.
  • All preferences persist locally (localStorage['codedash-sidebar-config'] v:1 + codedash-settings-tab). No server changes, no new endpoints, no network calls.

What's new

Surface Change
Sidebar HTML 3 <div class="sidebar-group" data-section> blocks with <button> headers; nested install-agents sub-group
src/frontend/sidebar-config.js (new) Pure UMD module: parseSidebarConfig / serializeSidebarConfig / isItemHidden / isSectionCollapsed / setItemHidden / setSectionCollapsed / loadFromStorage / saveToStorage / clearStorage / resetConfig + KNOWN_ITEM_KEYS allow-list
src/frontend/app.js applySidebarConfig, section-header click handler, toggleSidebarItem, two-stage onResetSidebarClick, renderSidebarSettingsGroup, onSettingsTabKey (keyboard nav), _announceSidebar (aria-live), tabbed renderSettings
src/frontend/styles.css .sidebar-group, .sidebar-section-header (32px min height), chevron rotation, .sidebar-subgroup indent, .settings-sidebar-* rows, .settings-tabs, .sr-only
Pre-paint script in index.html Tiny IIFE applies persisted collapsed class before sidebar markup paints — prevents flash of expanded state
test/sidebar-config.test.js (new) 34 node:test cases — all green

Accessibility

  • WAI-ARIA tablist with arrow-key navigation (Left/Right/Home/End), aria-controls, aria-labelledby, roving tabindex.
  • aria-expanded on section headers; aria-live="polite" status region announces show/hide toggles.
  • Settings entry is always visible — checkbox is disabled, isItemHidden short-circuits, setItemHidden refuses to write. Three-layer lockout protection.
  • prefers-reduced-motion suppresses chevron transition.
  • Section header contrast raised from --text-muted to --text-secondary (WCAG 1.4.3 AA).
  • Touch targets ≥32px on top sections, ≥28px on sub-sections (WCAG 2.5.8).

Security

  • localStorage payload capped at 64 KB (synchronous parse on init — DoS guard).
  • __proto__ / constructor / prototype keys explicitly blocked in sanitizeMap.
  • Config stores only booleans; item keys come from a hardcoded allow-list — no path lets user-controlled strings reach the DOM as markup or attribute-context JS.
  • All <a target="_blank"> get rel="noopener noreferrer".

Spec-driven artifacts

  • docs/design/sidebar-customization.md — SDD with full UX/a11y section
  • specs/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 verdict

Deferred (tracked in commit message)

  • Cache .sidebar-item NodeList in applySidebarConfig — premature optimization for ~30 elements
  • Count badge on collapsed "Install agents · 7" — cosmetic, follow-up PR

Test plan

  • node --test test/sidebar-config.test.js — 34/34 pass
  • Open dashboard → sidebar shows 3 collapsible sections; Install agents collapsed by default
  • Click "Workspace" header → collapses with chevron ▾→▸; reload → still collapsed
  • Settings → Sidebar → uncheck Leaderboard → hides instantly; reload → still hidden
  • Settings checkbox itself is disabled with tooltip "Settings is always visible"
  • Reset to defaults → first click arms (red "Click again to confirm" + Cancel); auto-disarm after 6s; second click resets
  • Hide every Workspace item → "All items hidden — manage in Settings" hint appears
  • DevTools: localStorage['codedash-sidebar-config'] = '{garbage' → reload → default layout, no error UI
  • Settings tab strip: Tab to focus, ← → Home End navigate between tabs; focus follows
  • Visit #leaderboard while Leaderboard hidden → view renders, sidebar link stays hidden
  • Tested in dark / light / monokai themes

@vakovalskii
Copy link
Copy Markdown
Owner

⚠️ Don't merge until rebased — GitHub shows mergeable but the actual diff against current main includes a lot of deletions that would silently undo recent merges.

git diff main pr216 --stat says this PR also removes:

  • src/repo-refresh.js (-440), src/repo-refresh-routes.js (-174), src/atomic.js (-36), test/repo-refresh*.test.js (-698), test/atomic.test.js (-94) → all from feat: background git fetch for connected repos (auto-refresh v1) #213 ("background git fetch")
  • test/frontend-escaping.test.js (-30), test/terminals-windows-launch.test.js (-35) → from fix: preserve Windows paths for Codex resume #214 ("Windows path fix")
  • docs/ARCHITECTURE.md (-107), docs/design/repo-auto-refresh.md (-308), specs/repo-auto-refresh.feature (-216), tasks/2026-05-12-repo-auto-refresh/plan.md (-403)
  • atomicWriteJson import + use in src/data.js

The branch looks like it was forked from main before #213/#214/#215/#217 landed (yesterday + this morning). Could you rebase onto current main and force-push?

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-lease

Once the diff drops to just the sidebar work (+/- in app.js, index.html, styles.css, the new sidebar-config.js + tests + docs), I'll re-review. Should be quick after that — the actual code looks great (sidebar-config.js with allow-list + 64 KB cap + proto-pollution guard + 34 tests). 🙏

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.
@NovakPAai NovakPAai force-pushed the feat/sidebar-customization branch from 7f5232e to 87386eb Compare May 18, 2026 07:44
@NovakPAai
Copy link
Copy Markdown
Collaborator Author

Rebased onto current main (now at 56a8da8). git diff main..HEAD --stat is clean — only the sidebar + tabbed Settings work (10 files, +1779/-148), no negative deltas against #213/#214/#217. Git auto-skipped the already-merged #215 cherry-pick. Local node --test test/sidebar-config.test.js test/repo-refresh.test.js → 50/50 ✅. CI re-running on the rebased head. Ready for re-review 🙏

Copy link
Copy Markdown
Owner

@vakovalskii vakovalskii left a comment

Choose a reason for hiding this comment

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

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.js pure UMD module with allow-list for KNOWN_ITEM_KEYS, 64KB localStorage cap, proto-pollution guard (__proto__/constructor/prototype blocked), booleans only in config
  • WAI-ARIA tablist with arrow-key nav + roving tabindex, aria-expanded, aria-live for 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! 🙏

@vakovalskii vakovalskii merged commit 654530c into main May 18, 2026
6 checks passed
@vakovalskii vakovalskii deleted the feat/sidebar-customization branch May 18, 2026 13:45
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.

3 participants