Context
While auditing the render architecture in #203, the left sidebar was identified as the highest-payoff decomposition target.
Currently left_sidebar.rs is a collection of &self render helpers called inline from AnotherOneApp::render. Any state change anywhere in the app triggers a full re-render of the sidebar even when its specific state (project list, task list, expand state, context menus) didn't change.
What to do
Mirror the existing RightSidebarPanel pattern exactly:
- Define a
SidebarSnapshot struct capturing everything the sidebar needs: projects, tasks, expanded repo IDs, UI state (pinned tasks, last active section), and the per-task menu/rename/delete-confirm state that currently lives on AnotherOneApp.
- Create
struct SidebarPanel implementing Render, storing the snapshot plus local interaction state (sidebar_task_menu, sidebar_task_rename, sidebar_task_delete_confirm, sidebar_task_last_click).
- Store
sidebar_panel: Entity<SidebarPanel> on AnotherOneApp.
- In the drain tick, compute a fresh
SidebarSnapshot only when project/task state actually changed (gate behind a dirty flag like sidebar_changed), push it to SidebarPanel and call cx.notify() on it. Terminal-output ticks must not notify the sidebar.
- Remove the sidebar-only fields from
AnotherOneApp (5–6 fields).
Why this is the right next step
RightSidebarPanel already proves the pattern works in this codebase.
- The sidebar's state drivers (project/task mutations, daemon
ProjectList pushes) are low-frequency (<1/sec) and already separated from terminal-output ticks by the drain loop.
- Sidebar context-menu state (
sidebar_task_menu, etc.) has zero cross-cutting dependencies — it can move directly into the new view.
project_store is the only broad dependency; a SidebarSnapshot severs it cleanly.
Context
While auditing the render architecture in #203, the left sidebar was identified as the highest-payoff decomposition target.
Currently
left_sidebar.rsis a collection of&selfrender helpers called inline fromAnotherOneApp::render. Any state change anywhere in the app triggers a full re-render of the sidebar even when its specific state (project list, task list, expand state, context menus) didn't change.What to do
Mirror the existing
RightSidebarPanelpattern exactly:SidebarSnapshotstruct capturing everything the sidebar needs: projects, tasks, expanded repo IDs, UI state (pinned tasks, last active section), and the per-task menu/rename/delete-confirm state that currently lives onAnotherOneApp.struct SidebarPanelimplementingRender, storing the snapshot plus local interaction state (sidebar_task_menu,sidebar_task_rename,sidebar_task_delete_confirm,sidebar_task_last_click).sidebar_panel: Entity<SidebarPanel>onAnotherOneApp.SidebarSnapshotonly when project/task state actually changed (gate behind a dirty flag likesidebar_changed), push it toSidebarPaneland callcx.notify()on it. Terminal-output ticks must not notify the sidebar.AnotherOneApp(5–6 fields).Why this is the right next step
RightSidebarPanelalready proves the pattern works in this codebase.ProjectListpushes) are low-frequency (<1/sec) and already separated from terminal-output ticks by the drain loop.sidebar_task_menu, etc.) has zero cross-cutting dependencies — it can move directly into the new view.project_storeis the only broad dependency; aSidebarSnapshotsevers it cleanly.