Skip to content

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

Open
NovakPAai wants to merge 2 commits into
mainfrom
feat/sidebar-customization
Open

feat(sidebar): group + customize sidebar items, tabbed Settings#216
NovakPAai wants to merge 2 commits 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

…sses (#209)

- Watch ~/.codex/history.jsonl, session_index.jsonl and per-day session
  dirs in _sessionsNeedRescan so new Codex sessions appear without
  restarting a long-running codbash server. Walk is performed once per
  rescan and reused in _updateScanMarkers to avoid a TOCTOU race.
- Prefer /proc/<pid>/cwd readlink on Linux; harden lsof fallback with a
  multiline regex (no leading-newline dependency), strip
  "(readlink: …)" annotations, and reject /proc/* pseudo-paths.
- Drop fallback-latest when cwd is known: emit sessionId='' and
  _sessionSource='unmatched' instead of confidently misattributing the
  process to an unrelated latest session. Pick the most recently active
  session among same-cwd candidates. Frontend now indexes unmatched
  entries by 'pid:<pid>' so they remain visible on the Agent Board.

Accepted lower-severity review notes:
- lsof multiline edge cases: -d cwd already constrains output to one FD
- mid-walk day-dir deletion: next poll rescans, throw is swallowed
- EACCES on ~/.codex: symmetric with other agents' silent skip
- stat-on-poll when no Codex installed: one existsSync, negligible
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.
@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). 🙏

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