/** * Menu-control concern for the desktop UI plugin. * * Owns: open/close orchestration, keyboard navigation, active-index * reconciliation, pane repaint dispatch, and aria-expanded management. * * `menus.ts` builds the DOM; this module drives it. The two concerns are * deliberately separate: menus.ts renders, menuControl.ts orchestrates. * * Integration: the plugin class constructs one `MenuControlState` + * `MenuControlRefs` bag after `buildDom()` and passes them (plus a small * set of callbacks) into every function here. */ import type { IVideoPlayer, VideoPlaylistItem } from '../../../index.js'; import type { ActivityState } from './activity.js'; import type { MenuFrameRefs, MenuRenderState, SubMenuId, SubtitleMenuAction } from './menus.js'; /** Mutable menu-control state threaded through every helper. */ export interface MenuControlState { menuOpen: boolean; currentSubMenu: SubMenuId | null; activeSubtitleIdx: number | null; activeAudioIdx: number; activeQualityIdx: number | 'auto'; playingQualityIdx: number | null; userPickedQuality: boolean; } export declare function makeMenuControlState(): MenuControlState; /** Button refs needed for aria-expanded management. */ export interface MenuControlRefs { settingsBtn: HTMLButtonElement; speedBtn: HTMLButtonElement; qualityBtn: HTMLButtonElement; subsBtn: HTMLButtonElement; audioBtn: HTMLButtonElement; playlistBtn: HTMLButtonElement; aspectRatioBtn: HTMLButtonElement; } export type MenuListen = (target: EventTarget, event: string, fn: (event: Event) => void) => void; /** * Narrow options surface menu-control needs from `DesktopUiOptions` — kept * structural (not the full options type) so this module doesn't couple to * every desktop-ui config field. */ export interface MenuRenderOpts { imageBaseUrl?: string; subtitleMenuActions?: ReadonlyArray; } /** Map a sub-menu id to its trigger button, or null for unmapped ids. */ export declare function menuTriggerBtn(id: SubMenuId, refs: MenuControlRefs): HTMLButtonElement | null; /** Set `aria-expanded` on the trigger button for a sub-menu (or the settings button for the main menu). */ export declare function setMenuTriggerExpanded(id: SubMenuId | null, expanded: boolean, refs: MenuControlRefs): void; /** Collapse all menu trigger buttons (aria-expanded = false). */ export declare function collapseAllTriggers(refs: MenuControlRefs): void; /** Open the main (top-level) settings menu. */ export declare function openMainMenu(state: MenuControlState, activityState: ActivityState, menus: MenuFrameRefs, refs: MenuControlRefs): void; /** Open a specific sub-menu pane by id and repaint it immediately. */ export declare function openSubMenu(id: SubMenuId, state: MenuControlState, activityState: ActivityState, menus: MenuFrameRefs, refs: MenuControlRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined, bumpActivity: () => void): void; /** Close all menus and restart the inactivity timer. */ export declare function closeAllMenus(state: MenuControlState, activityState: ActivityState, menus: MenuFrameRefs, refs: MenuControlRefs, bumpActivity: () => void): void; /** * Wire Up/Down arrow-key navigation through the open menu pane. * Propagation is stopped so the player's seek/volume key handlers * cannot fire while a menu is open. */ export declare function wireMenuKeyboardNav(state: MenuControlState, menus: MenuFrameRefs, listen: MenuListen): void; /** * Reconcile the locally-cached active track indexes with the player's * canonical state after a new item finishes loading (`mediaReady`). * * Reads the kit's actual selection via `subtitle()` / `audioTrack()` first — * those are the source of truth for plugin-rendered tracks (ASS via Octopus, * etc.) that never appear in the backend's native track lists. Falls back to a * default-track heuristic when the kit has no selection yet. */ export declare function syncActiveIndexes(state: MenuControlState, player: IVideoPlayer): void; /** * Build the `MenuRenderState` snapshot from cached indexes. * Active indexes are kept current by event handlers — read them as-is; do NOT * call `syncActiveIndexes` here or the user's manual pick would be overwritten * on every repaint. */ export declare function menuState(state: MenuControlState): MenuRenderState; /** * Repaint the named sub-menu pane with fresh data from the player. * `closeAllMenusFn` is the plugin's bound `closeAllMenus` — passed in so * pick-to-close actions in sub-panes can close the whole menu without this * module knowing about the plugin's full state. */ export declare function repaintPane(id: SubMenuId, state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void; /** Repaint the subtitles pane if it is currently open. */ export declare function repaintSubsIfOpen(state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void; /** Repaint the audio/language pane if it is currently open. */ export declare function repaintAudioIfOpen(state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void; /** Repaint the quality pane if it is currently open. */ export declare function repaintQualityIfOpen(state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void; /** Repaint the speed pane if it is currently open. */ export declare function repaintSpeedIfOpen(state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void; /** Repaint the playlist pane if it is currently open. */ export declare function repaintPlaylistIfOpen(state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void; /** Repaint the aspect-ratio pane if it is currently open. */ export declare function repaintAspectRatioIfOpen(state: MenuControlState, menus: MenuFrameRefs, player: IVideoPlayer, listen: MenuListen, closeAllMenusFn: () => void, opts: MenuRenderOpts | undefined): void;