mirror of
https://github.com/anomalyco/opentui.git
synced 2026-04-25 04:55:58 +03:00
[PR #715] [CLOSED] feat: Support multiple TUI renderers in a single process #1516
Labels
No labels
bug
core
documentation
feature
good first issue
help wanted
pull-request
question
react
solid
tmux
windows
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/opentui#1516
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/anomalyco/opentui/pull/715
Author: @jettptacek
Created: 2/19/2026
Status: ❌ Closed
Base:
main← Head:main📝 Commits (2)
70b3999feat: Support multiple TUI renderers in a single processa02beffFix: renderer-stack.ts for prettier📊 Changes
12 files changed (+556 additions, -119 deletions)
View changed files
📝
packages/core/src/animation/Timeline.ts(+47 -29)📝
packages/core/src/renderer.ts(+96 -29)📝
packages/core/src/testing/test-renderer.ts(+0 -1)➕
packages/core/src/tests/renderer.multi-instance.test.ts(+138 -0)📝
packages/core/src/zig.ts(+9 -0)📝
packages/core/src/zig/lib.zig(+4 -0)📝
packages/core/src/zig/renderer.zig(+70 -42)📝
packages/core/src/zig/tests/renderer_test.zig(+89 -0)📝
packages/solid/index.ts(+35 -14)📝
packages/solid/src/elements/hooks.ts(+2 -1)📝
packages/solid/src/reconciler.ts(+22 -3)➕
packages/solid/src/renderer-stack.ts(+44 -0)📄 Description
This PR is meant to be the start of the conversation of allowing opentui to render multiple TUIs. I have allowed multiple independent
CliRendererinstances to coexist within a single process. Previously, the Zig renderer used static/global output buffers and always wrote tostdout, meaning only one renderer could exist at a time. The timeline engine also only tracked a single renderer.I understand this is a large change..... that cuts across the native renderer, the TypeScript layer, and the Solid integration and I'm expecting that others might want a different implementation
I am using this right now in ssh server where I can make a different tui per client connected.
Changes
Zig renderer (
renderer.zig)vardeclarations to per-instance heap-allocated fieldsOutputBufferWriternow holds a*CliRendererpointer instead of referencing global statestd.fs.File.stdout()calls replaced with a per-instanceoutput_filefield (defaults to stdout)setOutputFd/fileFromFdto redirect a renderer's output to an arbitrary file descriptor, with cross-platform handle type supportFFI bindings (
lib.zig,zig.ts)setOutputFdthrough the FFI layerTypeScript renderer (
renderer.ts)remoteconfig flag: when true, the renderer skips process-level signal handlers (SIGWINCH,uncaughtException,beforeExit), stdout interception, exit listeners, andglobal.requestAnimationFrameoverwritingconfig.stdoutfd to the native renderer viasetOutputFd, with Windows_get_osfhandleconversion throughmsvcrt.dllprocessResizechanged fromprivatetopublicso callers can trigger resizes on specific renderer instancesTimeline engine (
Timeline.ts)TimelineEnginechanged from tracking a single renderer to aMap<CliRenderer, AttachedRenderer>where each renderer gets its own frame callback and live statedetach()can target a specific renderer or detach allSolid integration (
index.ts,reconciler.ts,hooks.ts,renderer-stack.ts)renderer-stack.tsmodule provides renderer lookup via a synchronous stack (during initial mount) and aWeakMapkeyed by Solid owner (for effect re-runs)useRenderer()falls back togetActiveRenderer()whenRendererContextis not availablerender()andtestRender()now callengine.detach(renderer)on destroyrender()return type changed toPromise<CliRenderer>Test renderer (
test-renderer.ts)@ts-expect-erroronprocessResizecall (now public)Tests
renderer.multi-instance.test.ts): independent coexistence, independent resize, independent render buffers, destroy isolation, remote signal handler behavior, timeline engine multi-attach/detachrenderer_test.zig):setOutputFdoutput redirect via pipe, two renderers writing to separate fds🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.