Skip to content

Demo:最小 TUI

工程化复现第五部分的组件模型 + 布局 + 差分渲染。源码在 pi-principles/src/tui/,入口在 pi-principles/play/tui.ts

工程化结构

text
pi-principles/src/tui/
├── component.ts   # Component 类型(render(width) → string[])
├── layout.ts      # 布局:把组件放进屏幕矩形,渲染成字符网格
└── renderer.ts    # 差分渲染:只重写变化行

关键实现

组件(component.ts

ts
export type Component = { render(width: number): string[] };
export function streamingComponent(text: () => string): Component;  // 流式打字组件
export function textComponent(line: string): Component;             // 静态文本组件

布局(layout.ts

ts
export function layout(components, width, height): Box[];   // 计算每个组件的矩形
export function renderFrame(boxes, width, height): string[]; // 渲染成字符网格

差分渲染(renderer.ts

ts
export function createScreen(): Screen {
	return {
		prev: [],
		emit(next, width) {
			for (let i = 0; i < next.length; i++) {
				if (this.prev[i] !== next[i]) {
					process.stdout.write(`\x1b[${i + 1};1H\x1b[2K${next[i]}`);  // 只重写变化行
				}
			}
			this.prev = next;
		},
	};
}

入口与运行

play/tui.ts 组装三个组件,模拟模型逐字输出,看差分渲染只重写最后一行:

bash
cd pi-principles
bun play/tui.ts          # 或 npm run tui

在真实终端里运行,你会看到前三行只画一次,只有最后一行在逐字追加、被反复重写

对应到真实源码

工程化 Demo真实 pi 源码
tui/component.tspackages/tui/src/tui.ts
tui/layout.tspackages/tui/src/layout.ts
tui/renderer.tspackages/tui/src/tui-main-screen.ts

理解要点

  • 组件是纯函数render(width) 只受宽度影响,才能被缓存、被差分。
  • 布局负责"放哪",组件负责"画什么"。
  • 差分只重写变化的行:终端高效的关键。

下一步:6.1 架构总览