/** * Conversation entrance-motion engine (feature: motion). * * Watches two host surfaces: * - The transcript chat rows — the host renders one `[data-chat-anchor-key]` * wrapper per message (user / assistant / tool, all kinds) inside a * `[data-chat-flow]` column that stays mounted across conversation switches. * - The sidebar session tree — `[role="treeitem"]` rows (workspace groups and * sessions) inside `[role="tree"]` containers. * * Every entrance runs imperatively through the Web Animations API: * - A CSS `@starting-style` transition only fires on an element's FIRST style * resolution, and the host forces one while it mounts a row (layout effects, * scroll-into-view, measurement) before this observer's microtask can mark the * element. The declarative start state therefore never applied and rows * appeared at rest. * - Web Animations runs whenever it is asked to, retargets from the live value * instead of restarting, and needs no forced reflow. `fill: 'backwards'` holds * the start frame across a stagger delay, so a row never flashes at its * resting style before its turn. * * Batch classification: a "load" batch (the container was empty before, or rows * were removed in the same batch = first render / conversation switch) gets a * per-row stagger so the surface cascades in instead of popping; an * "incremental" batch (streaming turns, older-history paging, search results, * group expand) gets no delay and, for the tree, no entrance at all — only * wholesale loads animate the sidebar. * * The observer starts before the settings scope resolves, buffering batches * until the feature state lands, so even a slow settings load still captures the * very first conversation render. */ import { type MotionStyle, type NewChatMotionStyle, type SidebarMotionStyle } from '../../shared/motionSettings.js'; /** Engine wiring: the current feature state (scope-driven). */ export interface MotionEngineState { /** Transcript entrance (rows + column panel), gated by motionEnabled. */ transcript: boolean; /** Sidebar tree entrance (initial load + group expand), gated by sidebarMotionEnabled. */ sidebar: boolean; /** Blank-session (new conversation) entrance, gated by newChatMotionEnabled. */ newChat: boolean; /** The entrance style new transcript rows should use. */ style: MotionStyle; /** * Whether transcript rows arrive by role (user message sideways, assistant * prose in the chosen style, process rows lightly) instead of every row using * the chosen style. Gated by motionRoleEntrance; false = one style for all. */ roleEntrance: boolean; /** The entrance style the blank-session (new conversation) dialog uses. */ newChatStyle: NewChatMotionStyle; /** The entrance style new sidebar tree items should use. */ sidebarStyle: SidebarMotionStyle; /** * Whether the current session is blank (a brand-new conversation): the * composer seat — which hosts the blank-session hero — animates in only for * these, so a new conversation's main dialog visibly appears while ordinary * switches leave the seat alone. */ blank: boolean; } /** Whether any motion toggle is on (the engine stays inert only when all are off). */ export declare function anyMotionEnabled(state: MotionEngineState): boolean; /** Engine wiring: reads the feature state and subscribes to its changes. */ export interface MotionEngineOptions { /** The current feature state (scope-driven). */ getState: () => MotionEngineState; /** Subscribe to state changes (settings scope); returns the disposer. */ subscribe: (listener: () => void) => () => void; } /** The installed engine handle: teardown + the session-switch replay signal. */ export interface MotionEngine { /** Tear the engine down (disconnect, unsubscribe, unmark rows). */ dispose: () => void; /** * Force-replay the entrance on every mounted row. Called on conversation * switches: the host remounts the whole transcript, and this covers every way * those rows may reach the DOM (including ones the observer cannot * correlate), so every open/switch animates — not just the first. */ notifySessionSwitch: () => void; } /** * Install the entrance-motion engine. Starts observing immediately (batches * buffer while disabled) and returns the engine handle: teardown plus the * session-switch replay signal (see {@link MotionEngine}). * @param options - feature-state access (scope-driven). */ export declare function installConversationEntrance(options: MotionEngineOptions): MotionEngine;