Building a TUI Library from Scratch: Part 7 - Text Diffs, Style Registry, and Real Demos

Things I learned:

After v0.0.11, I focused on one thing: stop sending all text every frame.

This part is mostly that migration plus style sync changes.

Cleanup before migration

I did quick cleanup first:

I also fixed p99 calculation in metrics to use interpolation instead of rough indexing:

function percentile(sorted: number[], p: number): number {
  const rank = p * (sorted.length - 1);
  const lower = Math.floor(rank);
  const upper = Math.ceil(rank);
  if (lower === upper) return sorted[lower]!;
  return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower);
}

This made comparison across runs more trustworthy.

Text registry migration

Old flow:

New flow:

First implementation used per-node FFI calls (upsert_text/delete_text). Worked, but too many calls.

Then I switched to batched ops with one sync_text_ops call per frame:

Rust applies all ops under one registry lock.

match op {
    TEXT_OP_UPSERT => registry.insert(node_id, text),
    TEXT_OP_DELETE => registry.remove(&node_id),
    _ => return 0,
}

Main result: text transfer scales with changed nodes, not total text nodes.

Metrics during transition

During transition, some snapshots got worse before they got better (~0.9ms avg vs older ~0.5ms samples).

Expected reasons:

Later snapshots in this period were around ~0.7ms avg. Not a straight line, but direction was correct.

Short test-driver experiment

I added a Unix socket test driver (ping, sleep, key, mouse, focused, snapshot, quit) to script interactions.

It was useful for quick black-box checks, but I later removed it in cleanup while simplifying runtime and examples.

Style sync architecture rewrite

Next bottleneck: style payload duplication.

Before:

After:

This introduced explicit schema files (style_schema.rs, src/style-schema.ts) and unlocked broader style support:

Wrapping and clipping fixes

I also fixed text wrapping and clipping behavior in Rust.

Problems were:

Key changes:

That improved:

Demos and docs

After those engine changes, I built more demos and proper docs:

It was a good validation pass because these examples touched many edge cases at once.

End of part 7

By end of this phase:

Next for me: scroll containers, richer text primitives, and more tail-latency cleanup on heavier scenes.