import { type OuroborosFrameName } from "./ouroboros-pet"; /** * ┌─ VIBRATO PET SPRITE SPEC ────────────────────────────────────────────────┐ * Pets are square pixel sprites drawn beside the composer. Everything here is * data: no PNGs or binary assets. Current pet frames are 16×16 and render into * the same terminal footprint. * * GRID RULES * - Every frame within a skin has the same square dimensions. * - `.` = transparent. Keep the outer columns transparent so the sprite sits * snug beside the input box (the widget reserves +1 column of slack). * * PALETTE (char → role) — see PALETTE for exact RGB: * .=transparent K=dark outline R=body red r=red highlight * V=visor screen G=visor glow(green) H=satgat straw h=satgat brim * b=belly tan A=antenna * * FRAME CATALOG (VibratoPixelFrameName → PIXEL_GRIDS): * base idle rest; also the dance "drop/settle" beat * gazeL eyes glance left ┐ idle loop (see vibrato-pet-widget IDLE_LOOP) * gazeR eyes glance right │ * flicker visor blink ┘ * flex both claws up + `^^`; dance accent + random idle flex burst * danceL left claw up + `><` + feet step left ┐ work loop (PARA_PARA_STEPS) * danceR right claw up + `^^` + feet step right ┘ * * RENDERING: buildVibratoPixelFrames({ protocol, cellWidthPx, cellHeightPx, * targetRows: 2 }) scales the art to 2 terminal rows and encodes each frame * once. Kitty uses a native `Y=` sub-cell drop (set by the widget) to sit on the * composer border; sixel uses transparent top padding; iTerm2 uses an inline PNG * sized to the reserved cell block. * * BEHAVIOR (timing, positioning, on/off) lives in * packages/coding-agent/src/modes/components/vibrato-pet-widget.ts. * * ADD A FRAME: draw the grid → add its name to VibratoPixelFrameName → register it in * PIXEL_GRIDS → reference it from an idle/work loop or a skin burst. * * ADD A PET (skin): append one PET_SKINS entry with its palette, frame registry, * base/idle/work animations and burst. The id flows into PetSkinId/PetMode * automatically; settings, `/pet`, and both selectors derive from PET_SKINS. * └────────────────────────────────────────────────────────────────────────┘ */ type Rgb = readonly [number, number, number]; export type Palette = Record; export declare const PET_SKIN_IDS: readonly ["red", "blue", "ouroboros"]; export type PetSkinId = (typeof PET_SKIN_IDS)[number]; /** Every pet mode: "off" plus each skin id, in menu order. */ export declare const PET_MODE_IDS: readonly ["off", "red", "blue", "ouroboros"]; export type PetMode = (typeof PET_MODE_IDS)[number]; /** Narrow an arbitrary string to a PetMode. */ export declare function isPetMode(value: string): value is PetMode; /** Resolve a persisted mode after a skin has been removed. Explicit "off" remains off. */ export declare function resolvePetMode(value: string): PetMode; /** Logical pixel-pet frame names shared by the overlay state machine. */ export type VibratoPixelFrameName = "base" | "gazeL" | "gazeR" | "flicker" | "flex" | "danceL" | "danceR" | "cry1" | "cry2" | "cry3"; export type PetFrameName = VibratoPixelFrameName | OuroborosFrameName; /** Para-para work dance beats: the working loop and each skin's burst "work-in" intro. */ export type VibratoGifFrameTuple = readonly [VibratoPixelFrameName, number]; export declare const PARA_PARA_STEPS: readonly VibratoGifFrameTuple[]; export declare const VIBRATO_IDLE_STEPS: ReadonlyArray; /** * A skin's idle burst: a short intro sequence, then an optional looping tail. It drives * BOTH the random live show-off AND the selector's preview demo, so give every skin a * real animation (reuse PARA_PARA_STEPS for a work-in intro) rather than one held frame. */ export interface PetBurst { /** Frames played once, in order, at the start of the burst. */ intro: ReadonlyArray; /** Frames cycled every `stepMs` for `ms` after the intro (a held or looping finish). */ tail?: { frames: readonly PetFrameName[]; stepMs: number; ms: number; }; } /** Everything that defines a pet skin: identity, UI copy, colors and behavior. */ export interface PetSkin { id: PetSkinId; /** Selector/settings label, e.g. "RedVibrato". */ label: string; /** One-line selector/settings description. */ description: string; palette: Palette; frames: Readonly>; baseFrame: PetFrameName; idle: ReadonlyArray; workEnter?: ReadonlyArray; work: ReadonlyArray; workExit?: ReadonlyArray; /** Idle burst animation played between quiet idle loops. */ burst: PetBurst; /** Optional variants that interrupt and then resume the work loop. */ workBursts?: readonly PetBurst[]; } /** Skin registry — the single source for palettes, behavior and selector/command copy. */ export declare const PET_SKINS: Record; /** Total burst duration (intro beats plus the looping tail). */ export declare function petBurstDurationMs(burst: PetBurst): number; /** The frame to show `elapsed` ms into a burst (`now` cycles the looping tail). */ export declare function petBurstFrame(burst: PetBurst, elapsed: number, now: number): PetFrameName; /** Test-only access to logical art; production rendering still uses encoded frames. */ export declare const __vibratoPetTestHooks: { getPixelGrid(name: PetFrameName, skin?: PetSkinId): string[]; }; export interface VibratoGifFrame { readonly name: PetFrameName; readonly delayMs: number; } export type VibratoGifTimeline = readonly VibratoGifFrame[]; export interface VibratoGifRectangle { readonly width?: number; readonly height?: number; } export interface VibratoGifDisplaySize { /** iTerm2 display width: bare numbers are terminal cells; strings may use px or auto. */ readonly width: number | string; /** iTerm2 display height: bare numbers are terminal cells; strings may use px or auto. */ readonly height: number | string; } export interface VibratoGifContentInset { /** Transparent top padding in source pixels. */ readonly topPx?: number; /** Transparent bottom padding in source pixels. */ readonly bottomPx?: number; } export interface VibratoPetGifArtifact { readonly bytes: Uint8Array; readonly base64: string; readonly width: number; readonly height: number; readonly frames: readonly VibratoGifFrame[]; readonly skin: PetSkinId; readonly multipart: readonly string[]; readonly tmuxDcs: readonly string[]; } export interface VibratoPetGifOptions { readonly skin?: PetSkinId; readonly timeline?: VibratoGifTimeline; readonly cellWidthPx?: number; readonly cellHeightPx?: number; readonly targetRows?: number; readonly rectangle?: VibratoGifRectangle; readonly displaySize?: VibratoGifDisplaySize; readonly contentInset?: VibratoGifContentInset; } export declare const idleTimeline: () => VibratoGifTimeline; export declare const workingTimeline: () => VibratoGifTimeline; export declare const burstTimeline: (skin?: PetSkinId) => VibratoGifTimeline; export declare const previewTimeline: (skin?: PetSkinId) => VibratoGifTimeline; export declare function encodeVibratoPetGif(input?: VibratoPetGifOptions | VibratoGifTimeline): VibratoPetGifArtifact; export declare function getVibratoPetGifCached(input?: VibratoPetGifOptions | VibratoGifTimeline): VibratoPetGifArtifact; export declare function getVibratoPetGifCacheStats(): { size: number; bytes: number; gifBytes: number; base64Bytes: number; multipartBytes: number; tmuxDcsBytes: number; evictions: number; }; export declare function resetVibratoPetGifCache(): void; export declare const clearVibratoPetGifCache: typeof resetVibratoPetGifCache; /** Encode a grid as a transparent SIXEL image, optionally bottom-aligned by top padding. */ export declare function encodeGridSixel(grid: string[], scale: number, topPaddingPx?: number, palette?: Palette): string; /** * Encode a grid as an iTerm2 inline PNG spanning a terminal cell block. * * The escape's `width`/`height` are the reserved cell-block footprint in * character cells (unitless numbers per the iTerm2 inline-images protocol). * iTerm2 resolves cells against its own live font metrics, so the sprite * scales with the real terminal geometry — including Retina, where iTerm2 * divides `Npx` values by the backing-scale factor and would render a fixed * pixel box at half size. */ export declare function encodeGridIterm2(grid: string[], scale: number, columns: number, rows: number, topPaddingPx?: number, bottomPaddingPx?: number, leftPaddingPx?: number, rightPaddingPx?: number, palette?: Palette): string; /** Encode a bottom-aligned grid as kitty raw RGBA at `scale`. */ export declare function encodeGridKitty(grid: string[], scale: number, imageId: number, cols: number, rows: number, topPaddingPx?: number, cellYOffsetPx?: number, leftPaddingPx?: number, rightPaddingPx?: number, palette?: Palette): string; export interface VibratoPixelFrames { /** escape payload per logical frame (drawn at the current cursor cell) */ frames: Record; /** protocol the frames were encoded for */ protocol: "sixel" | "kitty" | "iterm2"; /** Scaled sprite width before transparent cell-block padding. */ widthPx: number; /** Encoded raster height, including protocol-specific transparent padding. */ heightPx: number; columns: number; rows: number; /** terminal rows touched by the encoded raster, including pixel offset */ rasterRows: number; } /** * Build overlay pixel frames exactly `targetRows` terminal rows tall when the * terminal cells permit it. Each skin owns its source resolution so future * additions can opt into denser art without changing the terminal footprint. * * Geometry contract: * - `scale = max(1, targetRows * cellHeightPx / gridHeight)` * - `columns = ceil(scaledSpriteWidthPx / cellWidthPx)` * - `rows = ceil(scaledSpriteHeightPx / cellHeightPx)` * - the square sprite is centered in a `columns * cellWidthPx` PNG canvas * * iTerm2 receives unitless `width=columns;height=rasterRows`, so it resolves the * padded block with its live cell metrics. The PNG has that block's pixel aspect * ratio, allowing `preserveAspectRatio=0` without stretching the authored square * sprite. Kitty and Sixel retain their protocol-specific paths. */ export declare function buildVibratoPixelFrames(options: { protocol: "sixel" | "kitty" | "iterm2"; cellWidthPx: number; cellHeightPx: number; targetRows?: number; /** Transparent pixel offset above sixel art for sub-cell vertical placement. */ sixelTopPaddingPx?: number; /** Native sub-cell `Y=` pixel offset that drops the kitty sprite within its first cell. */ kittyCellYOffsetPx?: number; kittyImageId?: number; /** Additional transparent iTerm2-only top padding for sub-cell vertical alignment. */ iterm2TopPaddingPx?: number; /** Transparent iTerm2-only bottom padding inside the canvas. */ iterm2BottomPaddingPx?: number; /** Color skin for the sprite palette (default "red"). */ skin?: PetSkinId; }): VibratoPixelFrames; export {};