import type { ScrollBoxHandle } from '@hermes/ink' import type { MutableRefObject, ReactNode, RefObject, SetStateAction } from 'react' import type { PasteEvent } from '../components/textInput.js' import type { GatewayClient } from '../gatewayClient.js' import type { ImageAttachResponse } from '../gatewayTypes.js' import type { RpcResult } from '../lib/rpc.js' import type { Theme } from '../theme.js' import type { ApprovalReq, ClarifyReq, ConfirmReq, DetailsMode, Msg, PanelSection, SecretReq, SectionVisibility, SessionInfo, SlashCatalog, SudoReq, Usage } from '../types.js' export interface StateSetter { (value: SetStateAction): void } export type StatusBarMode = 'bottom' | 'off' | 'top' export type BusyInputMode = 'interrupt' | 'queue' | 'steer' // Single source of truth for indicator style names. Union type is // derived from this tuple so adding/removing a style only touches one // line — `useConfigSync` (validation) and `session.ts` (slash arg // validation + usage hint) both import it. export const INDICATOR_STYLES = ['ascii', 'emoji', 'kaomoji', 'unicode'] as const export type IndicatorStyle = (typeof INDICATOR_STYLES)[number] export const DEFAULT_INDICATOR_STYLE: IndicatorStyle = 'kaomoji' export interface SelectionApi { captureScrolledRows: (firstRow: number, lastRow: number, side: 'above' | 'below') => void clearSelection: () => void copySelection: () => Promise copySelectionNoClear: () => Promise getState: () => unknown version: () => number shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void shiftSelection: (dRow: number, minRow: number, maxRow: number) => void } export interface CompletionItem { display: string meta?: string text: string } export interface GatewayRpc { (method: string, params?: Record): Promise } export interface GatewayServices { gw: GatewayClient rpc: GatewayRpc } export interface GatewayProviderProps { children: ReactNode value: GatewayServices } export interface OverlayState { agents: boolean agentsInitialHistoryIndex: number approval: ApprovalReq | null clarify: ClarifyReq | null confirm: ConfirmReq | null modelPicker: boolean pager: null | PagerState picker: boolean secret: null | SecretReq skillsHub: boolean sudo: null | SudoReq } export interface PagerState { lines: string[] offset: number title?: string } export interface TranscriptRow { index: number key: string msg: Msg } export interface UiState { bgTasks: Set busy: boolean busyInputMode: BusyInputMode compact: boolean detailsMode: DetailsMode detailsModeCommandOverride: boolean info: null | SessionInfo inlineDiffs: boolean mouseTracking: boolean sections: SectionVisibility showCost: boolean showReasoning: boolean indicatorStyle: IndicatorStyle sid: null | string status: string statusBar: StatusBarMode streaming: boolean theme: Theme usage: Usage } export interface VirtualHistoryState { bottomSpacer: number end: number measureRef: (key: string) => (el: unknown) => void offsets: ArrayLike start: number topSpacer: number } export interface ComposerPasteResult { cursor: number value: string } export type MaybePromise = Promise | T export interface ComposerActions { clearIn: () => void dequeue: () => string | undefined enqueue: (text: string) => void handleTextPaste: (event: PasteEvent) => MaybePromise openEditor: () => Promise pushHistory: (text: string) => void removeQueue: (index: number) => void replaceQueue: (index: number, text: string) => void setCompIdx: StateSetter setHistoryIdx: StateSetter setInput: StateSetter setInputBuf: StateSetter setPasteSnips: StateSetter setQueueEdit: (index: null | number) => void syncQueue: () => void } export interface ComposerRefs { historyDraftRef: MutableRefObject historyRef: MutableRefObject queueEditRef: MutableRefObject queueRef: MutableRefObject submitRef: MutableRefObject<(value: string) => void> } export interface ComposerState { compIdx: number compReplace: number completions: CompletionItem[] historyIdx: null | number input: string inputBuf: string[] pasteSnips: PasteSnippet[] queueEditIdx: null | number queuedDisplay: string[] } export interface UseComposerStateOptions { gw: GatewayClient onClipboardPaste: (quiet?: boolean) => Promise | void onImageAttached?: (info: ImageAttachResponse) => void submitRef: MutableRefObject<(value: string) => void> } export interface UseComposerStateResult { actions: ComposerActions refs: ComposerRefs state: ComposerState } export interface InputHandlerActions { answerClarify: (answer: string) => void appendMessage: (msg: Msg) => void die: () => void dispatchSubmission: (full: string) => void guardBusySessionSwitch: (what?: string) => boolean newSession: (msg?: string) => void sys: (text: string) => void } export interface InputHandlerContext { actions: InputHandlerActions composer: { actions: ComposerActions refs: ComposerRefs state: ComposerState } gateway: GatewayServices terminal: { hasSelection: boolean scrollRef: RefObject scrollWithSelection: (delta: number) => void selection: SelectionApi stdout?: NodeJS.WriteStream } voice: { enabled: boolean recording: boolean setProcessing: StateSetter setRecording: StateSetter setVoiceEnabled: StateSetter } wheelStep: number } export interface InputHandlerResult { pagerPageSize: number } export interface GatewayEventHandlerContext { composer: { setInput: StateSetter } gateway: GatewayServices session: { STARTUP_RESUME_ID: string colsRef: MutableRefObject newSession: (msg?: string) => void resetSession: () => void resumeById: (id: string) => void setCatalog: StateSetter } submission: { submitRef: MutableRefObject<(value: string) => void> } system: { bellOnComplete: boolean stdout?: NodeJS.WriteStream sys: (text: string) => void } transcript: { appendMessage: (msg: Msg) => void panel: (title: string, sections: PanelSection[]) => void setHistoryItems: StateSetter } voice: { setProcessing: StateSetter setRecording: StateSetter setVoiceEnabled: StateSetter } } export interface SlashHandlerContext { composer: { enqueue: (text: string) => void hasSelection: boolean paste: (quiet?: boolean) => void queueRef: MutableRefObject selection: SelectionApi setInput: StateSetter } gateway: GatewayServices local: { catalog: null | SlashCatalog getHistoryItems: () => Msg[] getLastUserMsg: () => string maybeWarn: (value: unknown) => void } session: { closeSession: (targetSid?: null | string) => Promise die: () => void guardBusySessionSwitch: (what?: string) => boolean newSession: (msg?: string) => void resetVisibleHistory: (info?: null | SessionInfo) => void resumeById: (id: string) => void setSessionStartedAt: StateSetter } slashFlightRef: MutableRefObject transcript: { page: (text: string, title?: string) => void panel: (title: string, sections: PanelSection[]) => void send: (text: string) => void setHistoryItems: StateSetter sys: (text: string) => void trimLastExchange: (items: Msg[]) => Msg[] } voice: { setVoiceEnabled: StateSetter } } export interface AppLayoutActions { answerApproval: (choice: string) => void answerClarify: (answer: string) => void answerSecret: (value: string) => void answerSudo: (pw: string) => void clearSelection: () => void onModelSelect: (value: string) => void resumeById: (id: string) => void setStickyPrompt: (value: string) => void } export interface AppLayoutComposerProps { cols: number compIdx: number completions: CompletionItem[] empty: boolean handleTextPaste: (event: PasteEvent) => MaybePromise input: string inputBuf: string[] pagerPageSize: number queueEditIdx: null | number queuedDisplay: string[] submit: (value: string) => void updateInput: StateSetter } export interface AppLayoutProgressProps { showProgressArea: boolean } export interface AppLayoutStatusProps { cwdLabel: string goodVibesTick: number sessionStartedAt: null | number showStickyPrompt: boolean statusColor: string stickyPrompt: string turnStartedAt: null | number voiceLabel: string } export interface AppLayoutTranscriptProps { historyItems: Msg[] scrollRef: RefObject virtualHistory: VirtualHistoryState virtualRows: TranscriptRow[] } export interface AppLayoutProps { actions: AppLayoutActions composer: AppLayoutComposerProps mouseTracking: boolean progress: AppLayoutProgressProps status: AppLayoutStatusProps transcript: AppLayoutTranscriptProps } export interface AppOverlaysProps { cols: number compIdx: number completions: CompletionItem[] onApprovalChoice: (choice: string) => void onClarifyAnswer: (value: string) => void onModelSelect: (value: string) => void onPickerSelect: (sessionId: string) => void onSecretSubmit: (value: string) => void onSudoSubmit: (pw: string) => void pagerPageSize: number } export interface PasteSnippet { label: string path?: string text: string }