/** * The /update panel: one bounded surface over the launcher update * pipeline. The panel never decides versions itself — it renders the * launcher `update --json` probe (plan plus refusals), takes one * confirmation, then streams `update --apply` progress and reports the * result with a restart hint. Every alignment guarantee (host pinned to * the release peers line, companion plugins carried, downgrade and * local-checkout refusals) lives in the launcher and is only displayed * here. */ import { type ReactElement } from 'react'; import type { LauncherUpdatePlan, LauncherUpdateStatus } from '../update.ts'; /** Retained apply-progress lines (ring tail; npm output is ephemeral). */ export declare const UPDATE_OUTPUT_CAP = 800; /** Keep the newest UPDATE_OUTPUT_CAP lines of streamed update output. */ export declare function clipUpdateLines(lines: readonly string[]): readonly string[]; /** One in-flight `update --apply`. Closing the panel must not start a second. */ interface UpdateApplyJob { readonly promise: Promise; readonly lines: string[]; readonly lineListeners: Set<(line: string) => void>; } /** True while an apply child is alive, even if the /update panel is closed. */ export declare function isUpdateApplyRunning(): boolean; /** Subscribe to apply-running changes (frozen-hint "esc waits"). */ export declare function subscribeUpdateApplyRunning(listener: (running: boolean) => void): () => void; /** The live apply job, if any: lines already streamed plus the shared promise. */ export declare function currentUpdateApply(): UpdateApplyJob | undefined; /** * Run `apply` once. A second call while the child is alive reuses the same * promise and replays buffered lines — closing /update and opening it again * must not spawn a second `npm install`. */ export declare function runUpdateApply(apply: (onLine: (line: string) => void, plan: LauncherUpdatePlan) => Promise, plan: LauncherUpdatePlan, onLine?: (line: string) => void): Promise; /** Drop a leftover job between tests. */ export declare function resetUpdateApply(): void; /** One display row of the update surface. */ export interface UpdateRow { readonly key: string; readonly text: string; readonly tone?: 'ok' | 'warn' | 'error' | 'dim'; } /** The plan view derived from one probe: facts to show and whether apply may run. */ export interface UpdatePlanView { readonly rows: readonly UpdateRow[]; readonly runnable: boolean; } /** Rendered facts of one probe: current/target versions, actions, refusals. */ export declare function updatePlanView(status: LauncherUpdateStatus): UpdatePlanView; /** Panel lifecycle phases; the footer names the keys each phase accepts. */ export type UpdatePhase = 'probe' | 'error' | 'plan' | 'apply' | 'done'; /** Footer hint line per phase; the plan phase names the confirm key only when runnable. */ export declare function updateFooter(phase: UpdatePhase, runnable: boolean, upToDate: boolean, aheadOfRegistry?: boolean): string; /** * The /update surface: probe on open (and on r), confirm with enter/y, * stream the aligned apply, and land on a bounded result view. Escape is * locked while the apply child runs — killing npm mid-install is exactly * the half-updated state this command exists to prevent. */ export declare function UpdatePanel({ probe, apply, close, notify }: { /** Read-only probe of the launcher update status (never installs). */ probe: () => Promise; /** Run the aligned update; streams sanitized progress lines. */ apply: (onLine: (line: string) => void, plan: LauncherUpdateStatus['plan']) => Promise; /** Close the panel (App keeps ownership of the flag). */ close: () => void; /** One bounded cross-surface notice (phase completions). */ notify: (text: string, tone?: 'info' | 'warning' | 'error') => void; }): ReactElement; export {};