Download src/ink/frame.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 4.21 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/ink/frame.ts
- Command line
-
hf download hf://chenbhao/codev/src/ink/frame.ts
-
curl -L -o frame.ts https://huggingface.co/chenbhao/codev/resolve/main/src/ink/frame.ts
4.21 kB
| import type { Cursor } from './cursor.js' | |
| import type { Size } from './layout/geometry.js' | |
| import type { ScrollHint } from './render-node-to-output.js' | |
| import { | |
| type CharPool, | |
| createScreen, | |
| type HyperlinkPool, | |
| type Screen, | |
| type StylePool, | |
| } from './screen.js' | |
| export type Frame = { | |
| readonly screen: Screen | |
| readonly viewport: Size | |
| readonly cursor: Cursor | |
| /** DECSTBM scroll optimization hint (alt-screen only, null otherwise). */ | |
| readonly scrollHint?: ScrollHint | null | |
| /** A ScrollBox has remaining pendingScrollDelta β schedule another frame. */ | |
| readonly scrollDrainPending?: boolean | |
| } | |
| export function emptyFrame( | |
| rows: number, | |
| columns: number, | |
| stylePool: StylePool, | |
| charPool: CharPool, | |
| hyperlinkPool: HyperlinkPool, | |
| ): Frame { | |
| return { | |
| screen: createScreen(0, 0, stylePool, charPool, hyperlinkPool), | |
| viewport: { width: columns, height: rows }, | |
| cursor: { x: 0, y: 0, visible: true }, | |
| } | |
| } | |
| export type FlickerReason = 'resize' | 'offscreen' | 'clear' | |
| export type FrameEvent = { | |
| durationMs: number | |
| /** Phase breakdown in ms + patch count. Populated when the ink instance | |
| * has frame-timing instrumentation enabled (via onFrame wiring). */ | |
| phases?: { | |
| /** createRenderer output: DOM β yoga layout β screen buffer */ | |
| renderer: number | |
| /** LogUpdate.render(): screen diff β Patch[] (the hot path this PR optimizes) */ | |
| diff: number | |
| /** optimize(): patch merge/dedupe */ | |
| optimize: number | |
| /** writeDiffToTerminal(): serialize patches β ANSI β stdout */ | |
| write: number | |
| /** Pre-optimize patch count (proxy for how much changed this frame) */ | |
| patches: number | |
| /** yoga calculateLayout() time (runs in resetAfterCommit, before onRender) */ | |
| yoga: number | |
| /** React reconcile time: scrollMutated β resetAfterCommit. 0 if no commit. */ | |
| commit: number | |
| /** layoutNode() calls this frame (recursive, includes cache-hit returns) */ | |
| yogaVisited: number | |
| /** measureFunc (text wrap/width) calls β the expensive part */ | |
| yogaMeasured: number | |
| /** early returns via _hasL single-slot cache */ | |
| yogaCacheHits: number | |
| /** total yoga Node instances alive (create - free). Growth = leak. */ | |
| yogaLive: number | |
| } | |
| flickers: Array<{ | |
| desiredHeight: number | |
| availableHeight: number | |
| reason: FlickerReason | |
| }> | |
| } | |
| export type Patch = | |
| | { type: 'stdout'; content: string } | |
| | { type: 'clear'; count: number } | |
| | { | |
| type: 'clearTerminal' | |
| reason: FlickerReason | |
| // Populated by log-update when a scrollback diff triggers the reset. | |
| // ink.tsx uses triggerY with findOwnerChainAtRow to attribute the | |
| // flicker to its source React component. | |
| debug?: { triggerY: number; prevLine: string; nextLine: string } | |
| } | |
| | { type: 'cursorHide' } | |
| | { type: 'cursorShow' } | |
| | { type: 'cursorMove'; x: number; y: number } | |
| | { type: 'cursorTo'; col: number } | |
| | { type: 'carriageReturn' } | |
| | { type: 'hyperlink'; uri: string } | |
| // Pre-serialized style transition string from StylePool.transition() β | |
| // cached by (fromId, toId), zero allocations after warmup. | |
| | { type: 'styleStr'; str: string } | |
| export type Diff = Patch[] | |
| /** | |
| * Determines whether the screen should be cleared based on the current and previous frame. | |
| * Returns the reason for clearing, or undefined if no clear is needed. | |
| * | |
| * Screen clearing is triggered when: | |
| * 1. Terminal has been resized (viewport dimensions changed) β 'resize' | |
| * 2. Current frame screen height exceeds available terminal rows β 'offscreen' | |
| * 3. Previous frame screen height exceeded available terminal rows β 'offscreen' | |
| */ | |
| export function shouldClearScreen( | |
| prevFrame: Frame, | |
| frame: Frame, | |
| ): FlickerReason | undefined { | |
| const didResize = | |
| frame.viewport.height !== prevFrame.viewport.height || | |
| frame.viewport.width !== prevFrame.viewport.width | |
| if (didResize) { | |
| return 'resize' | |
| } | |
| const currentFrameOverflows = frame.screen.height >= frame.viewport.height | |
| const previousFrameOverflowed = | |
| prevFrame.screen.height >= prevFrame.viewport.height | |
| if (currentFrameOverflows || previousFrameOverflowed) { | |
| return 'offscreen' | |
| } | |
| return undefined | |
| } | |