From 681ce24dc6cc0dee12cd6fc3780a0443706bed4f Mon Sep 17 00:00:00 2001 From: flare576 Date: Fri, 27 Feb 2026 13:58:59 -0600 Subject: [PATCH] feat(tui): add width_method config option for ZWJ emoji handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some terminals (tmux, older terminals) do not render ZWJ emoji sequences as joined glyphs, causing layout corruption when the width measurement assumes joining. OpenTUI's Zig layer has a 'no_zwj' mode that handles this correctly, but it was previously unreachable from the JavaScript API. This depends on anomalyco/opentui# which exposes 'no_zwj' in the WidthMethod type and wires it through the FFI boundary. Add a 'width_method' option to the TUI config schema that passes through to OpenTUI's render() call. Users experiencing layout corruption from ZWJ emoji can opt in via their opencode.json: { "tui": { "width_method": "no_zwj" } } No default is changed — existing behavior is fully preserved. --- packages/opencode/src/cli/cmd/tui/app.tsx | 1 + packages/opencode/src/cli/cmd/tui/config/tui-schema.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index ea742f699708..27fb64d88214 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -74,6 +74,7 @@ function rendererConfig(_config: TuiConfig.Info): CliRendererConfig { return { externalOutputMode: "passthrough", targetFps: 60, + widthMethod: _config.width_method, gatherStats: false, exitOnCtrlC: false, useKittyKeyboard: {}, diff --git a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts index ed79e8e52418..4c658a9e2067 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui-schema.ts @@ -24,6 +24,12 @@ export const TuiOptions = z.object({ .optional() .describe("Control diff rendering style: 'auto' adapts to terminal width, 'stacked' always shows single column"), mouse: z.boolean().optional().describe("Enable or disable mouse capture (default: true)"), + width_method: z + .enum(["unicode", "wcwidth", "no_zwj"]) + .optional() + .describe( + "Character width calculation method. Use 'no_zwj' if ZWJ emoji sequences cause layout corruption in your terminal (common with tmux and older terminals). Default: 'unicode'", + ), }) export const TuiInfo = z