The TUI initialization within the `pi-tui` package (specifically in `packages/tui/src/tui.ts`) involves the following main steps: 1. **Constructor (`constructor(terminal: Terminal, showHardwareCursor?: boolean)`)**: * It takes a `Terminal` instance as a dependency, which abstracts the actual terminal interface (handling input/output, cursor movement, etc.). * It initializes the `TUI` class as a `Container`, meaning it can hold and render child components. * It sets up internal state variables for managing previous screen content (`previousLines`, `previousWidth`, `previousHeight`), focus (`focusedComponent`), input listeners, render scheduling, and overlay management. * It optionally configures whether to show the hardware cursor. 2. **Starting the TUI (`start(): void`)**: * Sets a flag `stopped` to `false`. * Calls `this.terminal.start()` to begin listening for terminal events (input and resize). This method likely sets up raw mode for the terminal and registers callbacks for input (`handleInput`) and rendering requests (`requestRender`). * Hides the hardware cursor. * Calls `this.queryCellSize()` to inquire about the terminal's cell dimensions, which is crucial for image rendering within the TUI. * Requests an initial render by calling `this.requestRender()`. 3. **Handling Input (`handleInput(data: string): void`)**: * This method is called by the `Terminal` instance whenever input is received. * It first iterates through registered `inputListeners` (used for things like keybindings or command parsing). * It checks for specific terminal responses, like cell size queries (used for image rendering). * It handles a global debug key combination (`Shift+Ctrl+D`). * It manages focus: if the currently focused component is an overlay that has become invisible (due to terminal resize or other logic), focus is reassigned. * It forwards input to the `focusedComponent` if it has an `handleInput` method and opts in to receive the input (or if it's not a key release event). * It calls `requestRender()` if the input processing leads to a change in the UI state. 4. **Requesting and Performing Renders (`requestRender()`, `scheduleRender()`, `doRender()`)**: * `requestRender()`: This is the public method to signal that the UI needs to be redrawn. It uses `process.nextTick` to schedule `scheduleRender()`, which ensures renders are throttled to a minimum interval (16ms) to avoid performance issues and flickering. * `doRender()`: This is the core rendering logic. * It first calls `this.render(width)` (inherited from `Container`) to get the current lines from all components. * It then composites any active overlays on top of these lines using `compositeOverlays()`. * It extracts the hardware cursor position if a marker is present in the rendered output (for IME support). * It applies necessary ANSI escape codes for resetting and ensures lines are properly terminated. * It performs a differential render if possible (calculating changes from `previousLines` and writing only the necessary escape codes to the terminal). * If a full re-render is necessary (due to terminal resize, content shrinking in specific modes, or initial render), it clears the screen and redraws everything. * Finally, it updates internal state (`previousLines`, `previousWidth`, etc.) and positions the hardware cursor using `positionHardwareCursor()`. In essence, the TUI is initialized by setting up the terminal interface, hiding the cursor, and then entering a render loop that continuously redraws the screen based on component states, handling input and terminal events to update the display. Overlays and focus management are key parts of the rendering and input handling logic.The TUI initialization within the `pi-tui` package primarily occurs within the `TUI` class, detailed in `packages/tui/src/tui.ts`. The main steps are: 1. **Instantiation (`new TUI(terminal: Terminal, ...)`):** * A `TUI` instance is created, requiring a `Terminal` instance (which abstracts the underlying terminal capabilities like input/output and cursor control). * It inherits from `Container`, allowing it to manage a tree of child UI components. * Internal state is initialized for managing the screen buffer, cursor position, focus, input listeners, and rendering schedule. 2. **Starting the TUI (`start(): void`):** * This method is called to activate the TUI. * It sets an internal `stopped` flag to `false`. * It calls `this.terminal.start()`, which likely puts the terminal into raw mode and sets up callbacks for handling input (`handleInput`) and requesting re-renders (`requestRender`). * The hardware cursor is hidden (`this.terminal.hideCursor()`). * `this.queryCellSize()` is called to determine the pixel dimensions of a character cell, essential for rendering images within the TUI. * An initial render is requested via `this.requestRender()`. 3. **Input Handling (`handleInput(data: string): void`):** * When input data is received from the terminal (via the `Terminal` instance), this method is invoked. * It first processes any registered `inputListeners`. * It specifically handles terminal responses, such as cell size queries. * It includes a global debug key handler (`Shift+Ctrl+D`). * It manages focus: if the currently focused component is no longer visible (e.g., due to terminal resizing), focus is reassigned to another component. * Finally, it forwards the input to the currently `focusedComponent` if that component has an `handleInput` method and is configured to receive it. If input processing results in UI changes, `requestRender()` is called. 4. **Rendering (`requestRender()`, `scheduleRender()`, `doRender()`):** * `requestRender()`: This is the primary method for signaling that the UI needs to be updated. It schedules a render operation using `process.nextTick` and debounces/throttles renders to ensure a minimum interval (16ms) between them via `scheduleRender()`. * `doRender()`: This is the core rendering function: * It first obtains the current terminal dimensions (`width`, `height`). * It calls `this.render(width)` (inherited from `Container`) to get the lines of text representing the current state of all UI components. * If overlays are active, it composites them onto the base content using `compositeOverlays()`. * It then performs a differential render. It compares the newly rendered lines with the `previousLines` and sends only the necessary ANSI escape codes to the terminal to update only the changed portions of the screen, minimizing flicker. * If a full re-render is necessary (e.g., on initial startup, terminal resize, or when content shrinks and `clearOnShrink` is enabled), it clears the entire screen and redraws all content. * It updates the `hardwareCursorRow` and `cursorRow` based on the rendered content and positions the actual terminal cursor. * Finally, it stores the `newLines` as `previousLines` for the next render cycle and updates the terminal's state. In summary, the TUI initialization sets up the core rendering loop, input handling, and state management, relying on a `Terminal` abstraction to interact with the actual console.