source dump of claude code
23
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 124 lines 4.2 kB view raw
1import type { Cursor } from './cursor.js' 2import type { Size } from './layout/geometry.js' 3import type { ScrollHint } from './render-node-to-output.js' 4import { 5 type CharPool, 6 createScreen, 7 type HyperlinkPool, 8 type Screen, 9 type StylePool, 10} from './screen.js' 11 12export type Frame = { 13 readonly screen: Screen 14 readonly viewport: Size 15 readonly cursor: Cursor 16 /** DECSTBM scroll optimization hint (alt-screen only, null otherwise). */ 17 readonly scrollHint?: ScrollHint | null 18 /** A ScrollBox has remaining pendingScrollDelta — schedule another frame. */ 19 readonly scrollDrainPending?: boolean 20} 21 22export function emptyFrame( 23 rows: number, 24 columns: number, 25 stylePool: StylePool, 26 charPool: CharPool, 27 hyperlinkPool: HyperlinkPool, 28): Frame { 29 return { 30 screen: createScreen(0, 0, stylePool, charPool, hyperlinkPool), 31 viewport: { width: columns, height: rows }, 32 cursor: { x: 0, y: 0, visible: true }, 33 } 34} 35 36export type FlickerReason = 'resize' | 'offscreen' | 'clear' 37 38export type FrameEvent = { 39 durationMs: number 40 /** Phase breakdown in ms + patch count. Populated when the ink instance 41 * has frame-timing instrumentation enabled (via onFrame wiring). */ 42 phases?: { 43 /** createRenderer output: DOM → yoga layout → screen buffer */ 44 renderer: number 45 /** LogUpdate.render(): screen diff → Patch[] (the hot path this PR optimizes) */ 46 diff: number 47 /** optimize(): patch merge/dedupe */ 48 optimize: number 49 /** writeDiffToTerminal(): serialize patches → ANSI → stdout */ 50 write: number 51 /** Pre-optimize patch count (proxy for how much changed this frame) */ 52 patches: number 53 /** yoga calculateLayout() time (runs in resetAfterCommit, before onRender) */ 54 yoga: number 55 /** React reconcile time: scrollMutated → resetAfterCommit. 0 if no commit. */ 56 commit: number 57 /** layoutNode() calls this frame (recursive, includes cache-hit returns) */ 58 yogaVisited: number 59 /** measureFunc (text wrap/width) calls — the expensive part */ 60 yogaMeasured: number 61 /** early returns via _hasL single-slot cache */ 62 yogaCacheHits: number 63 /** total yoga Node instances alive (create - free). Growth = leak. */ 64 yogaLive: number 65 } 66 flickers: Array<{ 67 desiredHeight: number 68 availableHeight: number 69 reason: FlickerReason 70 }> 71} 72 73export type Patch = 74 | { type: 'stdout'; content: string } 75 | { type: 'clear'; count: number } 76 | { 77 type: 'clearTerminal' 78 reason: FlickerReason 79 // Populated by log-update when a scrollback diff triggers the reset. 80 // ink.tsx uses triggerY with findOwnerChainAtRow to attribute the 81 // flicker to its source React component. 82 debug?: { triggerY: number; prevLine: string; nextLine: string } 83 } 84 | { type: 'cursorHide' } 85 | { type: 'cursorShow' } 86 | { type: 'cursorMove'; x: number; y: number } 87 | { type: 'cursorTo'; col: number } 88 | { type: 'carriageReturn' } 89 | { type: 'hyperlink'; uri: string } 90 // Pre-serialized style transition string from StylePool.transition() — 91 // cached by (fromId, toId), zero allocations after warmup. 92 | { type: 'styleStr'; str: string } 93 94export type Diff = Patch[] 95 96/** 97 * Determines whether the screen should be cleared based on the current and previous frame. 98 * Returns the reason for clearing, or undefined if no clear is needed. 99 * 100 * Screen clearing is triggered when: 101 * 1. Terminal has been resized (viewport dimensions changed) → 'resize' 102 * 2. Current frame screen height exceeds available terminal rows → 'offscreen' 103 * 3. Previous frame screen height exceeded available terminal rows → 'offscreen' 104 */ 105export function shouldClearScreen( 106 prevFrame: Frame, 107 frame: Frame, 108): FlickerReason | undefined { 109 const didResize = 110 frame.viewport.height !== prevFrame.viewport.height || 111 frame.viewport.width !== prevFrame.viewport.width 112 if (didResize) { 113 return 'resize' 114 } 115 116 const currentFrameOverflows = frame.screen.height >= frame.viewport.height 117 const previousFrameOverflowed = 118 prevFrame.screen.height >= prevFrame.viewport.height 119 if (currentFrameOverflows || previousFrameOverflowed) { 120 return 'offscreen' 121 } 122 123 return undefined 124}