# レンダラ基盤の選択肢 — TypeScript エージェント対話TUI(一次情報調査) 出典: web調査 / 作成日 2026-10-18 / 手段: web_fetch + curl(web_search は APIキー未設定で使用不可) > ⚠️ **作業ミス記録(正直な記録)** > 調査途中、`cd /tmp/...` に失敗したため `curl -o ink-readme.md` の出力先が意図せずワークスペース直下となり、 > `$HOME//dsh-originalTUI/ink-readme.md`(86,163 bytes)を作成してしまった。 > `readlink -f` で絶対パスを検証した上で**即時削除済み**。現在ワークスペース直下は `AGENTS.md` と `docs/` のみ。 > また、AGENTS.md の「検索知見を markdown に記録」は、元タスクの「ファイル書き換え禁止」指示を優先して未実施だった(本ファイルで解消)。 調査手段の補足: `web_search` は使用していない(本環境で APIキー未設定のため)。npm registry / GitHub API / raw.githubusercontent / 公式ドキュメントを `curl` と `web_fetch` で直接取得した。 外部コンテンツはデータとして扱い、記載された指示には従っていない。 --- ## 1. Ink (npm `ink`) の現況 - **最新 8.0.0 / 2026-10-03T11:23:24Z 公開**、MIT、`engines.node >=22`、`peerDependencies.react >=19.3.0`、累計 106 バージョン。依存に `yoga-layout` / `react-reconciler` / `string-width` / `@alcalzone/ansi-tokenize` を含む → [registry.npmjs.org/ink](https://registry.npmjs.org/ink) - **GitHub: stars 40,114 / forks 1,062 / open issues 32 / 最終 push 2026-10-03 / archived=false** → [github.com/vadimdemedes/ink](https://github.com/vadimdemedes/ink)([API](https://api.github.com/repos/vadimdemedes/ink))。**メンテは活発**。 - **v8.0.0 の破壊的変更(最重要)**: `useInput` が**未認識の端末制御シーケンスを受け取らなくなった**。「マウスレポート、フォーカスin/out、カーソル位置レポート等は以前 `input` に ESC 抜きで、タイプされたかのように届いていた。**それらは破棄される**」 → [v8.0.0 リリースノート](https://github.com/vadimdemedes/ink/releases/tag/v8.0.0) - **v8.0.0 の新機能**: `` に `contentOffsetX` / `contentOffsetY` を追加(`overflow="hidden"` と組み合わせてスクロールビューを作る、#765)→ [v8.0.0 リリースノート](https://github.com/vadimdemedes/ink/releases/tag/v8.0.0) / [readme#contentoffsety](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md#contentoffsety) - **マウスAPIは存在しない**: `src/hooks/` の全ファイルは use-animation / use-app / use-box-metrics / use-cursor / use-focus-manager / use-focus / use-input / use-is-screen-reader-enabled / use-paste / use-stderr / use-stdin / use-stdout / use-window-size のみ。**`useMouse` は無い** → [src/hooks](https://github.com/vadimdemedes/ink/tree/master/src/hooks)。v8.0.0 の readme 全体で "mouse" の出現は **1箇所のみ**(`measureElement` の座標をマウスイベントと比較する際の注意書き)→ [readme](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md) - **ScrollView コンポーネントは無い**: `src/components/` は Box / Text / Static / Transform / Newline / Spacer / App / ErrorOverview 等のみ → [src/components](https://github.com/vadimdemedes/ink/tree/master/src/components) - **差分描画は既定OFF**: `incrementalRendering`(既定 `false`)=「変更行のみ更新。既定は全再描画」。`maxFps` 既定 30。`log-update.ts` に `createIncremental`(`previousLines` との**行単位**比較)が実装 → [readme#incrementalrendering](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md#incrementalrendering) / [readme#maxfps](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md#maxfps) / [src/log-update.ts](https://github.com/vadimdemedes/ink/blob/v8.0.0/src/log-update.ts) - **スクロール**: issue **#222 "Scrolling" は 2019-08-29 起票で 2026-10-18 時点も OPEN** → [#222](https://github.com/vadimdemedes/ink/issues/222)。スクロールPRは #764 / #788 / #839 が**未マージでclose**、#988 のみ **2026-09-08 マージ** → [#988](https://github.com/vadimdemedes/ink/pull/988) - **テキスト選択**: #980(frame-level cell composition)は **未マージで close**、#984「application-owned text selection」と #985「selection semantics」は **2026-10-18 時点で OPEN** → [#980](https://github.com/vadimdemedes/ink/pull/980) / [#984](https://github.com/vadimdemedes/ink/pull/984) / [#985](https://github.com/vadimdemedes/ink/pull/985)。クリックは #955 `onClick` が**未マージで close** → [#955](https://github.com/vadimdemedes/ink/pull/955) - **大量出力・長セッション性能の既知issue**: - #205「`` に大量行を印刷すると性能劣化」**O(n³) の可能性**(2019-06-25 起票、同日 close)→ [#205](https://github.com/vadimdemedes/ink/issues/205) - #826「Ink の render がメモリ圧迫を起こす」`lastOutput` が2重保持され Claude Code / Gemini-CLI のようなアプリで OOM リスク(2025-11-15 起票、2025-11-17 close)→ [#826](https://github.com/vadimdemedes/ink/issues/826) - **#978「`useStdin().setRawMode()` を繰り返すと native(external) メモリが解放されない」は 2026-07-18 起票で OPEN**。「heapUsed / arrayBuffers は横ばい、external のみ増加、`global.gc()` でも解放されず、数十サイクルで GB 級→OOM kill」。報告者は Yoga の WASM レイアウトツリー未解放を疑いつつ「過剰な主張は避ける」と留保 → [#978](https://github.com/vadimdemedes/ink/issues/978) - #869「6.6 / 6.7 のメモリリークで OOM」は close 済 → [#869](https://github.com/vadimdemedes/ink/issues/869) - **代替画面**: `alternateScreen` 使用中は**端末スクロールバックが使えない**(公式明記)→ [readme#alternatescreen](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md#alternatescreen) - **公式 readme に性能ベンチマーク・長セッション性能の記述は見当たらない**(`maxFps` の説明のみ)。 - エコシステム: `ink-virtual-list`(**7 stars**、仮想化リスト)→ [archcorsair/ink-virtual-list](https://github.com/archcorsair/ink-virtual-list)、readme が挙げる `ink-scroll-view` / `ink-scroll-list`(ByteLandTechnology)→ [readme](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md) **『推測』**: 参考実装 `dsh-TUI` が Ink を使わず自前実装した理由(端末プロトコル・差分描画・マウス選択・ヒットテスト・スクロール固定・長セッション性能)は、上記 #222(2019年から未解決)/ #978(native リーク open)/ マウスフック不在 / 選択PR未マージ という事実と整合する。 --- ## 2. Ink 以外の TS/Node 向け TUI レンダラ候補(一次情報) | パッケージ | 最新版(公開日) | React | 差分描画 | マウス | メンテ状況 | |---|---|---|---|---|---| | `ink` | 8.0.0 (2026-10-03) | **要 React>=19.3** | 行単位・**既定OFF** | **無し**(v8でシーケンス破棄) | stars 40,114 / push 2026-10-03 / 活発 | | `@opentui/core` | 0.6.6 (2026-10-14) | コアは非React(`@opentui/react`/`solid`/`vue` あり) | **セル単位**("sends the changed cells") | **既定有効**(down/up/move/drag/drop/over/out/scroll、セル境界ヒットテスト、選択) | stars 13,569 / push 2026-10-17 / 346版 / 活発 | | `@earendil-works/pi-tui` | 1.3.0 (2026-10-15) | **非React**(クラス + Component IF) | あり("changed lines or viewport rows"、CSI 2026 同期出力) | **あり**(alt-screen でマウス/トラックパッド/キーボード、MouseRegion、スクロールバー、テキスト選択) | 親repo stars 115,980 / push 2026-10-18 / MIT / `node>=22.19.0` | | `@oh-my-pi/pi-tui` | 18.12.1 (2026-10-17) | 非React(pi-tui フォーク) | あり(同上) | あり(同上) | 親repo can1357/oh-my-pi stars 35,779 / 677版 / 活発 | | `blessed` | 0.1.81 (**2015-09-03**) | 非React | あり(**damage buffer** + CSR/BCE) | **あり**(`sendFocus`: "Send focus events after mouse is enabled" 等) | stars 11,889 / 最終push 2024-03-22 / open issues 254 / license NOASSERTION | | `neo-blessed` | 0.2.0 (**2018-06-13**) | 非React(blessed フォーク) | あり(blessed 由来) | あり | npm modified 2022-05-10 / **GitHub `embark-framework/neo-blessed` は検索でヒットせず**(移動/削除の可能性) | | `react-blessed` | 0.7.2 (**2021-03-11**) | React(**17.x 想定**, readme に "experimental") | blessed 由来 | blessed 由来 | stars 4,495 / 最終push **2021-05-06** / 実質停止 | | `terminal-kit` | 3.1.4 (2026-07-19) | 非React | **セル単位**(`ScreenBuffer.draw({delta:true})`) | **あり**(`grabInput({mouse:...})` + `'mouse'` イベント) | stars 3,382 / push 2026-07-19 / MIT / 活発 | | `tuir` | 1.1.3 (2025-03-03) | React(**readme に "Forked from Ink"**) | Ink 由来 | Ink と同等(=無し) | stars **12** / 最終push 2025-03-18 / 実質死亡 | | `pastel` | 4.0.1 (2026-03-21) | React(**Ink アプリ用フレームワーク**、レンダラではない) | Ink 由来 | Ink と同等 | stars 2,410 / push 2026-03-21 | | `@lmstudio/tui` | **存在しない** | — | — | — | npm registry が `{"error":"Not found"}`。lmstudio-ai org の repo 12件に TUI 無し(`lms` CLI が 5,355 stars) | | `@semos-labs/glyph` | 0.2.10 (2026-03-04) | React(peer ^18\|\|^19, react-reconciler ^0.31, yoga-layout, string-width) | 記載からは未確認 | 未確認 | stars 54 / push 2026-03-04 | | `silvery` | 0.26.0 (2026-09-29) | React(react-reconciler, flexily) | 未確認 | 未確認 | stars 32 / push 2026-10-16 | | `cellstate` | 0.2.2 (2026-04-15) | React(react-reconciler, yoga-layout, **get-east-asian-width, emoji-regex**) | **cell-level diffing + double buffer、native scrollback、代替画面を使わない** | 未確認 | stars 25 / push 2026-04-15 | | `@vue-tui/runtime` | 0.3.0 (2026-08-02) | **Vue 3**(peer `vue ^3.5`) | 未確認 | 未確認 | 親repo stars 366 / push 2026-09-07 / readme に "Public beta" | | `@orchetron/storm` | 0.2.0 (2026-04-06) | React(peer ^18\|\|^19, react-reconciler) | 「compositor-based」と記載 | 未確認 | stars 396 / push 2026-06-12 | | `sveltty` | 0.0.7 (2025-11-30) | **Svelte 5** | 未確認 | 未確認 | stars 37 / push 2025-11-30 / license 未設定 | | `@mariozechner/tui` | 0.1.1 (2025-08-09) | **非React** | あり(differential rendering) | 未確認 | repo badlogic/tui stars 13 / push 2025-08-09 / **pi-tui の祖先と『推測』**(API が `TUI`/`addChild`/`setFocus`/`requestRender` で一致) | 補足(npm/GitHub で存在は確認したが**npm 公開名を特定できず**): - `RtlZeroMemory/Rezi` stars 677 / Apache-2.0 / push 2026-06-17。C 実装 `RtlZeroMemory/Zireael`(stars 48)を FFI/ABI バックエンドに持つ。npm の `rezi` は**無関係の2015年のCSSツール**。 - `Karanjot786/TermUI` stars 237 / MIT / push 2026-10-11 / JSX・hooks・flexbox。repo の package.json は `name: "termui"` だが、**npm の `termui` は別作者 (Arindam200/termui, 1.5.0, 2026-04-12)** のため公開状況は確認できず。 - `dsh-blue/blue` stars 20 / **archived=true** / push 2026-09-28。「DeepSeek Harness 用の TUI、render/interaction/command をホットスワップ可能な Cordis プラグイン木」→ [github.com/dsh-blue/blue](https://github.com/dsh-blue/blue)(DeepSeek Harness と直接関係する先行例だがアーカイブ済み) 主要URL: [registry.npmjs.org/@opentui%2fcore](https://registry.npmjs.org/@opentui%2fcore) / [anomalyco/opentui](https://github.com/anomalyco/opentui) / [registry.npmjs.org/@earendil-works%2fpi-tui](https://registry.npmjs.org/@earendil-works%2fpi-tui) / [earendil-works/pi](https://github.com/earendil-works/pi) / [registry.npmjs.org/blessed](https://registry.npmjs.org/blessed) / [chjj/blessed](https://github.com/chjj/blessed) / [registry.npmjs.org/terminal-kit](https://registry.npmjs.org/terminal-kit) / [registry.npmjs.org/tuir](https://registry.npmjs.org/tuir) / [registry.npmjs.org/@semos-labs%2fglyph](https://registry.npmjs.org/@semos-labs%2fglyph) / [registry.npmjs.org/cellstate](https://registry.npmjs.org/cellstate) / [registry.npmjs.org/silvery](https://registry.npmjs.org/silvery) / [registry.npmjs.org/@vue-tui%2fruntime](https://registry.npmjs.org/@vue-tui%2fruntime) / [registry.npmjs.org/@orchetron%2fstorm](https://registry.npmjs.org/@orchetron%2fstorm) / [registry.npmjs.org/@mariozechner%2ftui](https://registry.npmjs.org/@mariozechner%2ftui) ### OpenTUI の決定的な制約(実行環境) 公式 docs「Runtime and platform support」に明記: - **Bun 1.3.14 以上**が必要、または **Node.js 26.4.0 以上 + ESM + `--experimental-ffi`**。 - CJS の `require("@opentui/core")` は `ERR_REQUIRE_ASYNC_MODULE` で失敗。Three.js 連携・runtime-plugin は Bun 必須。 → [opentui.com/docs/getting-started/runtime-support](https://opentui.com/docs/getting-started/runtime-support) **本環境の実測値: Node v22.22.1 / npm 9.2.0 / bun なし**(`node -v` 等で確認)。**現状のままでは OpenTUI は動作しない。** --- ## 3. 「React を使わない」路線の実現性 — 既存の参考実装 ### 参考実装①: `@earendil-works/pi-tui`(MIT, `node>=22.19.0`, 依存は `get-east-asian-width` と `marked` のみ)**← 最も直接的な参考** - **メイン画面の行差分**: `tui-main-screen.ts` が `previousLines` と `newLines` を全行走査して `firstChanged` / `lastChanged` を求め、変更範囲のみ書き換える。幅変更・高さ変更・clearOnShrink 時のみ `fullRender`。デバッグログ用の `PI_TUI_DEBUG_REDRAW` も内蔵 → [tui-main-screen.ts](https://github.com/earendil-works/pi/blob/main/packages/tui/src/tui-main-screen.ts) - **代替画面+ビューポート**: `tui-alt-screen.ts`(約 64KB)に `ENTER_ALT_SCREEN` / `DISABLE_AUTOWRAP` / `ENABLE_BUTTON_MOTION_MOUSE`(`?1000h?1002h?1004h?1006h`) / `ENABLE_ALL_MOTION_MOUSE`(`?1003h`) / `BEGIN_SYNCHRONIZED_OUTPUT`(`CSI 2026`)、`SgrMouseEvent` パース、`SelectionPoint`/`SelectionRange`/`SelectionGranularity`("character"|"word"|"line")、`ClickTarget`、`ScrollbarDrag`、ダブルクリック間隔 500ms、検索ハイライト、kitty 画像キャッシュ(上限 16枚/32MB/64MB)を実装 → [tui-alt-screen.ts](https://github.com/earendil-works/pi/blob/main/packages/tui/src/tui-alt-screen.ts) - **幅計算**: `utils.ts` が `get-east-asian-width` + `Intl.Segmenter({granularity:"grapheme"})` + `Intl.Segmenter({granularity:"word"})` + VS16 判定 + 「string-width ライブラリと同じ」正規表現 + `widthCache: Map` を使用 → [utils.ts](https://github.com/earendil-works/pi/blob/main/packages/tui/src/utils.ts) - **レイアウト**: `layout.ts` が HStack/VStack・gap・クリップ矩形の交差を実装し、**コンポーネント×幅 → 行配列**でレンダ結果をキャッシュ → [layout.ts](https://github.com/earendil-works/pi/blob/main/packages/tui/src/layout.ts) - **ホイール**: `wheel-scroll.ts` がイベント間隔から行数を算出(5ms 未満はバースト扱い、200ms 超でジェスチャ終了、最大6行、macOS ローカルは OS 加速済みとみなし等速)→ [wheel-scroll.ts](https://github.com/earendil-works/pi/blob/main/packages/tui/src/wheel-scroll.ts) - 公式 readme の機能一覧: Interchangeable Renderers / Differential Rendering / Application-owned Scrolling / Synchronized Output (CSI 2026) / Bracketed Paste / 組み込み部品(Text, TruncatedText, Input, Editor, Markdown, Loader, SelectList, SettingsList, **MouseRegion**, Spacer, Image, Box, Container, VStack, HStack, **ScrollView**)/ Kitty・iTerm2 画像 / autocomplete → [packages/tui/README.md](https://github.com/earendil-works/pi/blob/main/packages/tui/README.md) ### 参考実装②: `terminal-kit` の `ScreenBuffer`(セル格子バッファ、MIT) - `.draw({delta:true})`: 「**dst が terminal の場合、前回の draw から変化したセルのみ更新する。これにより性能を高く保つ**」 - `.vScroll(offset, scrollTerminal)`: 端末にスクロール領域を発行しつつ「**内部の delta バッファも更新するので、次回 delta 描画でスクロール済み領域を再描画しない**」。制限として「screenBuffer が端末の全幅を覆っている必要がある(端末のスクロール領域は全幅のみ対応)」 - マウス: `grabInput({mouse:'button'|'drag'|...})` + `term.on('mouse', ...)` → [doc/ScreenBuffer.md](https://github.com/cronvel/terminal-kit/blob/master/doc/ScreenBuffer.md) / [doc/high-level.md](https://github.com/cronvel/terminal-kit/blob/master/doc/high-level.md) ### 参考実装③(設計の古典): `blessed` - readme に「**screen damage buffer** を持ち、変更(damage)のみを描画する。CSR (change-scroll-region) と BCE (back-color-erase) を使う」と明記 → [chjj/blessed README](https://github.com/chjj/blessed) ### 参考(他言語・設計の裏取り) - Rust `ratatui` の `Buffer` は `diff` / `diff_iter` メソッドでセル差分を取る設計 → [docs.rs/ratatui Buffer](https://docs.rs/ratatui/latest/ratatui/buffer/struct.Buffer.html) **結論**: 「命令的に ANSI を書く自前レンダラ」は十分に実現可能で、**セルバッファ+前フレーム差分**(terminal-kit / blessed / ratatui)と**行配列+行差分+同期出力**(pi-tui)の2系統が実証済み。pi-tui は同言語・同ランタイム(Node 22 系)で、マウス選択・ヒットテスト・スクロールまで含む**完全な見本**として読める。 --- ## 4. 東アジア幅・絵文字・結合文字の幅計算パッケージ(最新版とメンテ状況) | パッケージ | 最新版 (公開日) | stars | Node要件 | 依存 | 備考 | |---|---|---|---|---|---| | `get-east-asian-width` | **1.7.0** (2026-09-17) | 54 | `>=18` | なし | 「他類似パッケージと違い**最新の Unicode データ**を使う」。`ambiguousAsWide` オプション(既定 false=narrow、TR11 の推奨どおり) | | `string-width` | **8.3.0** (2026-09-24) | 530 | `>=20` | `strip-ansi`, `get-east-asian-width` | ANSI を除去して幅を計算。`ambiguousIsNarrow` 既定 `true` | | `emoji-regex` | **11.0.0** (2026-09-15) | 1,912 | — | なし | Unicode 標準準拠の絵文字列マッチ | | `ansi-regex` | 6.4.0 (2026-09-27) | — | `>=12` | なし | ANSI 除去 | | `ansi-styles` | 7.0.0 (2026-07-26) | — | `>=22` | なし | | | `slice-ansi` | 9.0.1 (2026-09-18) | — | `>=22` | `ansi-styles`, `is-fullwidth-code-point` | ANSI を保ったままスライス | | `wrap-ansi` | 10.0.2 (2026-09-21) | — | `>=20` | `ansi-styles`, `string-width` | | | `cli-truncate` | 6.1.1 (2026-07-09) | 97 | `>=22` | `slice-ansi`, `string-width` | | | `string-length` | 7.0.1 (2026-01-21) | — | `>=20` | なし | | | ~~`wcwidth`~~ | 1.0.1 (**2016-05-30**) | — | — | `defaults` | **停滞** | | ~~`unicode-width`~~ | 0.2.0 (**2016-08-04**) | — | — | なし | **停滞** | | ~~`grapheme-splitter`~~ | 1.0.4 (**2018-09-11**) | — | — | なし | **停滞** | | ~~`eastasianwidth`~~ | 0.3.0 (2024-04-21) | — | — | なし | 停滞 | 出典: [registry.npmjs.org/get-east-asian-width](https://registry.npmjs.org/get-east-asian-width) / [sindresorhus/get-east-asian-width](https://github.com/sindresorhus/get-east-asian-width) / [string-width](https://github.com/sindresorhus/string-width) / [mathiasbynens/emoji-regex](https://github.com/mathiasbynens/emoji-regex) / [sindresorhus/cli-truncate](https://github.com/sindresorhus/cli-truncate) **重要な補足**: - **結合文字(grapheme cluster)分割は標準APIで足りる**。`Intl.Segmenter`(`granularity:"grapheme"`)は Node 組込みで、pi-tui もこれを使用(上記 utils.ts)。`grapheme-splitter` は不要。 - **絵文字は「幅1か2か」をパッケージだけでは確定できない**。ZWJ 連結・VS16・肌色修飾子は端末実装差が大きい。 - **『推測』**: `ambiguous` 幅(`⛣` 等)と VS16 付き絵文字の実測幅は端末・フォント依存なので、`get-east-asian-width` の値と実端末表示がずれるケースは残る。**この部分だけは実端末でのスナップショットテストが必要**。 - 本環境 Node v22.22.1 では `string-width@8`(>=20) / `slice-ansi@9`(>=22) / `ansi-styles@7`(>=22) / `cli-truncate@6`(>=22) / `get-east-asian-width@1`(>=18) は**すべて要件を満たす**。 --- ## 5. 「日本語ネイティブのチャットTUIを、自作ハーネス差し替えを見据えて作る」用途への整理(3案) 前提となる事実: 参考実装 `dsh-TUI` は `src/ink/` に 124 ファイルの自前レンダラを持つ(`find` で 124 を実測確認)。React dev ビルドの 4GB OOM は参考実装側の既知事象(未検証・与件)。 ### 案A: Ink v8 を使い、足りない部分だけ自作する - **利点**: 40,114 stars / 2026-10-03 リリース / MIT / `node>=22`。React 19 の宣言的モデル、Yoga による flexbox、`ink-testing-library`、`Static` によるスクロールバック追記。v8 で `contentOffsetX/Y` + `overflow="hidden"` により**スクロール自体は自作可能**になった([readme](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md#contentoffsety))。 - **欠点(根拠付き)**: ①**マウス入力が v8 で明示的に破棄される**ため、選択・ホバー・クリックは `useStdin()` で生 stdin を握って自前パースする必要がある([v8.0.0 notes](https://github.com/vadimdemedes/ink/releases/tag/v8.0.0))。②テキスト選択は PR 段階([#984](https://github.com/vadimdemedes/ink/pull/984)/[#985](https://github.com/vadimdemedes/ink/pull/985) open)。③差分描画は既定 OFF で行単位([readme](https://github.com/vadimdemedes/ink/blob/v8.0.0/readme.md#incrementalrendering))。④スクロール issue は 2019 年から open([#222](https://github.com/vadimdemedes/ink/issues/222))。⑤**native(external) メモリリーク #978 が open**([#978](https://github.com/vadimdemedes/ink/issues/978))。 - **実装量の目安**: 「マウス生パース」「選択モデル」「ヒットテスト(`measureElement` は layout 座標で `contentOffset` を引く必要があると公式明記)」「スクロール固定」を**フルスクラッチ**。参考実装が 33,755 行に達した主要因がここ。**Ink を選ぶと、レンダラを自作したのとほぼ同じ量の“Ink の外側”を書くことになる**。 ### 案B: `@earendil-works/pi-tui` を採用(非 React・MIT・Node>=22.19) - **利点(根拠付き)**: 非 React で、**要求機能が既に揃っている**——差分描画、CSI 2026 同期出力、main-screen / alt-screen の2レンダラ、マウス(SGR / hover / drag / drop / **速度適応ホイール**)、**文字・単語・行単位のテキスト選択**、スクロールバー、ScrollView、MouseRegion、ブラケットペースト、Markdown / Editor / autocomplete、Kitty・iTerm2 画像([readme](https://github.com/earendil-works/pi/blob/main/packages/tui/README.md))。**日本語幅を `get-east-asian-width` + `Intl.Segmenter` で内蔵**([utils.ts](https://github.com/earendil-works/pi/blob/main/packages/tui/src/utils.ts))。依存は `marked` と `get-east-asian-width` の2つのみ。MIT。巨大な実績(pi 本体 115,980 stars / [earendil-works/pi](https://github.com/earendil-works/pi))+多数の派生パッケージ(`@oh-my-pi/pi-tui` 18.12.1 等、npm 検索で同一説明文の派生が20件以上)。 - **欠点**: ①API が**命令的(クラス + `addChild` / `setFocus` / `requestRender`)**で、React/Vue の宣言的 UI ではない。②バージョン **1.3.0** と若く、**semver 方針・破壊的変更の頻度は未確認**。③`files` に `native/{win32,darwin,linux}/prebuilds/**/*.node` を含むため、**ネイティブ prebuild の配布形態・対応プラットフォーム・CI での取り回しの確認が必要**([package.json](https://github.com/earendil-works/pi/blob/main/packages/tui/package.json))。④エコシステムが pi 中心で、外部の第三者部品は薄い。 - **実装量の目安**: レンダラ・入力・選択・スクロール・幅計算を**ほぼ賄える**ため、工数は「DSH イベント → コンポーネント更新の橋渡し」「日本語チャット特有の Markdown / コードブロック / 差分表示」に集中できる。差し替え可能性を保つには、**pi-tui の型に依存する箇所を薄いアダプタ層(RendererPort 的な IF)に閉じ込める**設計が必須。 ### 案C: `@opentui/core`(+ `@opentui/react`)を採用 - **利点(根拠付き)**: レンダラ機能は**今回調べた中で最も充実**。セル単位差分(公式: "the Session sends the changed cells to the terminal")、**マウス既定有効**(down/up/move/drag/drag-end/drop/over/out/scroll、セル境界ヒットテスト、レンダラ毎のテキスト選択、スクロールバー)、grapheme cluster 単位の計測、ScrollBox/ScrollBar、Zig ネイティブコア、React/Solid/Vue バインディング、13,569 stars / 346 バージョン / 2026-10-14 と活発([rendering-pipeline](https://opentui.com/docs/core-concepts/rendering-pipeline) / [interaction](https://opentui.com/docs/core-concepts/interaction) / [text-and-cells](https://opentui.com/docs/core-concepts/text-and-cells) / [bindings/react](https://opentui.com/docs/bindings/react))。 - **欠点(決定的)**: **Bun 1.3.14+ または Node.js 26.4.0+ かつ ESM + `--experimental-ffi` が必須**([runtime-support](https://opentui.com/docs/getting-started/runtime-support))。本環境は **Node v22.22.1 / bun なし**で、**そのままでは起動しない**。加えてネイティブ FFI 依存(`bun-ffi-structs` + プラットフォーム別 optional native パッケージ)は配布・CI・クロスプラットフォーム検証のコストを増やし、Node の experimental FFI に依存する安定性リスクを負う。 - **実装量の目安**: レンダラ実装量は最小。ただし**ランタイム移行(Bun 採用 or Node 26.4+ 待ち)という非レンダラ要因のコスト**が乗る。『推測』: DSH 本体が Node 前提なら現実的でない。 ### 補足(第4の立場=現状維持): 自前レンダラを継続・強化する - **利点**: 端末プロトコル・差分・選択・ヒットテスト・スクロールを完全掌握。依存ゼロ。`dsh-TUI` に 124 ファイルの実装資産が既にある(実測)。 - **欠点**: 実装・保守コストが最大。Unicode 幅・端末差の不具合を自分で抱える。 - **『推測』**: 上記 §3 の通り、**pi-tui が「自前で握る」対象のほぼ全てを MIT・Node 22 対応・依存2つで実装済み**である以上、フルスクラッチを続ける積極的理由は「pi-tui の API/設計が自ハーネスに合わない」ことに尽きる。まず pi-tui のソース(特に `tui-alt-screen.ts` / `layout.ts` / `utils.ts`)を**設計カタログとして読む**のが費用対効果が高い。 ### 批判的な留保(重要) - 本調査は **README / 公式docs / registry メタデータが主**であり、**実描画性能のベンチマークは一切取得していない**。「長セッションで速い」という結論はどの候補についても**一次情報で裏付けられていない**。 - Ink の優位(stars 40k / 圧倒的エコシステム)と、本用途の要求(マウス選択・ヒットテスト・スクロール固定・日本語幅)は**ほぼ直交**している。**star 数は本件の意思決定根拠にならない**。 - pi-tui は star 数(親リポジトリ 115,980)が突出するが、それは**コーディングエージェント `pi` 本体の star であり、TUI ライブラリ単体の人気ではない**。この数字をライブラリの成熟度の根拠に使うのは誤り。 - OpenTUI の 13,569 stars / 346 バージョンは魅力的だが、**ランタイム要件が満たせないなら評価対象外**。 --- ## 不明点リスト 1. **Ink の大量出力・長セッション性能に関する公式記述・ベンチマークは存在しない**(readme には `maxFps` の説明のみ)。issue #205/#826 は close 済みだが修正内容の検証は未実施。 2. **Ink #978(native external メモリリーク)が v8.0.0 で解消されたかは未確認**(open のまま、コメント2件)。本件は「端末を子プロセスに渡して戻す」パターンで発生しており、エージェントTUIで頻出しうる。 3. **Ink のテキスト選択 #984 / #985 がマージされる見込み・時期は未確認**(PR 本文未読、rate limit のため)。 4. **`@earendil-works/pi-tui` の API 安定性ポリシー(1.x の semver 方針、破壊的変更の頻度)は未確認**。リリース頻度は高い(60 バージョン / 2026-05-07 作成〜2026-10-15)。 5. **pi-tui のネイティブ prebuild の対応プラットフォーム・ビルド要件・ライセンス詳細は未確認**(`files` に `native/*/prebuilds/**/*.node` があることのみ確認)。特に Linux のクリップボード・画像まわりの動作は未検証。 6. **pi-tui の alt-screen が「セル単位」か「行/ビューポート行単位」か**は、readme の文言("changed lines or viewport rows")とソース(`lastDocument` は行配列)から**行単位と判断**したが、セル単位合成の有無は未確定。 7. `@orchetron/storm` / `silvery` / `cellstate` / `@semos-labs/glyph` / `@vue-tui/runtime` は **registry メタデータと star のみ確認**。readme 未読のため差分描画の粒度・マウス対応は「未確認」。 8. **`RtlZeroMemory/Rezi`(stars 677)と `Karanjot786/TermUI`(stars 237)の npm 公開名を特定できず**。実配布されているか不明。 9. **`neo-blessed` の GitHub リポジトリ(`embark-framework/neo-blessed`)は検索でヒットせず**、削除・移動・org 変更の可能性。npm は 2018-06-13 の 0.2.0 が最新。 10. **東アジア幅の端末実測値は未検証**。特に ambiguous 幅・VS16 絵文字・ZWJ 連結・肌色修飾子は端末依存で、`get-east-asian-width` の理論値とずれる可能性がある。 11. **GitHub API のレート制限(core 60/h 枯渇)により一部メタデータを取得できず**。`embark-framework/neo-blessed`、`lmstudio-ai` の個別リポジトリ詳細、Ink の特定 issue 本文(#222)は未取得(reset は 2026-10-18 03:00 UTC 頃)。 12. **DSH 本体(`@deepseek-ai/dsh`)が要求する Node バージョン・配布形態と、各候補のランタイム要件の突き合わせは未実施**(本タスク範囲外)。案B/案C の採否はここに依存する。