perf: reduce UI render fanout and scroll jitter

- Cut broad render fanout across the app by replacing shared-store whole-object subscriptions with leaf selectors, memoizing hot chrome boundaries, and isolating disabled global providers from live session/message state. This keeps header controls, composer toolbars, side panels, and other non-hot UI surfaces from repainting on every assistant update or keystroke.

- Rework sidebar session ordering so recent, project groups, and worktree groups derive from one ordering source while avoiding streaming-time thrash. The sidebar now uses a stabilized session snapshot, preserves structural identity for unchanged rows, reads live row status/details per session, and applies a one-shot sort bump on idle->busy instead of continuously resorting during activity.

- Fix chat/input scroll instability by separating viewport-resize handling from message-growth handling, disabling conflicting native scroll anchoring, and stopping textarea autosize from collapsing on every growth keystroke. This removes the multiline typing jiggle during streaming and reduces unnecessary composer rerenders.

- Also gate voice context wiring behind voice-mode enablement and codify the learned render/scroll/order anti-patterns in AGENTS.md so future changes avoid the same classes of regressions.
This commit is contained in:
Bohdan Triapitsyn
2026-04-04 02:19:55 +03:00
parent c5497e9823
commit 8587db89b1
42 changed files with 1713 additions and 1047 deletions
@@ -43,21 +43,22 @@ export const UnifiedControlsDrawer: React.FC<UnifiedControlsDrawerProps> = ({
onOpenModel,
onOpenEffort,
}) => {
const {
providers,
currentProviderId,
currentModelId,
currentVariant,
setProvider,
setModel,
setCurrentVariant,
getCurrentModelVariants,
getModelMetadata,
} = useConfigStore();
const { addRecentModel, addRecentEffort, recentEfforts } = useUIStore();
const providers = useConfigStore((state) => state.providers);
const currentProviderId = useConfigStore((state) => state.currentProviderId);
const currentModelId = useConfigStore((state) => state.currentModelId);
const currentVariant = useConfigStore((state) => state.currentVariant);
const setProvider = useConfigStore((state) => state.setProvider);
const setModel = useConfigStore((state) => state.setModel);
const setCurrentVariant = useConfigStore((state) => state.setCurrentVariant);
const getCurrentModelVariants = useConfigStore((state) => state.getCurrentModelVariants);
const getModelMetadata = useConfigStore((state) => state.getModelMetadata);
const addRecentModel = useUIStore((state) => state.addRecentModel);
const addRecentEffort = useUIStore((state) => state.addRecentEffort);
const recentEfforts = useUIStore((state) => state.recentEfforts);
const { recentModelsList } = useModelLists();
const currentSessionId = useSessionUIStore((s) => s.currentSessionId);
const { saveAgentModelForSession, saveAgentModelVariantForSession } = useSelectionStore();
const saveAgentModelForSession = useSelectionStore((state) => state.saveAgentModelForSession);
const saveAgentModelVariantForSession = useSelectionStore((state) => state.saveAgentModelVariantForSession);
const sessionAgentName = useContextStore((state) =>
currentSessionId ? state.getSessionAgentSelection(currentSessionId) : null
);