fix(ui): stop the leftover xterm viewport painting a strip under the terminal (src/styles.css, docs) - #79
Conversation
…terminal 端末の下辺に出ていた横方向の帯と、右端のスクロールバーの矢印を消す。原因は 実測で特定した。実際のスタイルシートと index.html の DOM からブラウザ上に 採寸用のページを組み、そこで箱を測っている。 xterm 6 のスクロールは `.xterm-scrollable-element` が持ち、`.xterm-viewport` は誰も測らない空の `div` として残っている。それでも xterm 自身のスタイル シートはこの要素へ二つの指定を当てたままであり、どちらも保守されていない ——テーマの届かない `#000` の塗りと、ネイティブのスクロールバーを 15px 分 確保する `overflow-y: scroll` である。端末の箱は `height: 100%` で面いっぱい へ伸ばしてあり、xterm が描くのは行の整数倍であるから、`行数 × 行の高さ` を 引いた余りは誰も描かない帯として下辺に残る。そこをこの空の要素が黒で塗り、 右端にはネイティブのスクロールバーの下向き矢印が出ていた。 帯は割り算の余りであり、面の高さとともに動く。実測で 600〜900px の窓に対して 4.44px から 14.44px である。新しく生まれたものではなく、#68 が 800px の窓で 6.63px から 14.44px へ動かした。矢印の高さに届かなかったものがちょうど一つ分 になった時点で、縁ではなくスクロールバーとして読まれる。 補正の側は壊れていない。`fit` が測るのは `.term` であり、そこから出る桁数と 行数は実際に描かれている面と一致する。修正の前後どちらでも 126×23 に対して 960×345(1280×800 の窓)であり、`fitShown` の呼び出し口にも触れていない。 端末の使える面積も変えていない。直すのは何も描かない要素であるため、 `.xterm-viewport` へ `display: none` を当てる。塗りや `overflow` を上書き する形は採らない。その形では次の面の高さが作る余りの上に帯がまた立つ。 同じ規則に並んでいた `.xterm-viewport` と `.xterm-screen` の `height: 100%` も外す。前者は四辺で位置指定済み、後者はレンダラのインライン `height` が 勝つため、どちらも何も変えていなかった。 #77 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
smileygames
left a comment
There was a problem hiding this comment.
自己レビュー結果: PASS
#77 の受け入れ基準に対して差分を確認し、原因の主張を独立に裏取りした。
原因の裏取り
issue の前提に書いた仮説(.xterm-screen が viewport より広くなる)は外れており、実装者が実測で否定している。仮説を潰した上で別の原因に到達しているので、その筋を追認する。
node_modules/@xterm/xterm/css/xterm.css を直接読んで確認した。.xterm .xterm-viewport に background-color: #000(ハードコード)と overflow-y: scroll、そして position: absolute + top/right/bottom/left: 0 が乗っている。.xterm の箱いっぱいに黒い層が敷かれ、その右端に native のスクロールバーが確保される。
.xterm-screen はこの中ではなく兄弟である(lib/xterm.js の生成箇所で、viewport と screen が同じ fragment へ別々に append されている)。したがって screen が描く rows × cell height の下に残る帯だけが黒く見える。実機の見え方と一致する。
インストール済みは @xterm/xterm 6.0.0。スクロールは .xterm-scrollable-element 側にあり、xterm-viewport のクラス名は JS 中に生成箇所の一度しか出てこない。display: none で壊れる経路が無いことを、以下まで見て確認した。
OverviewRulerRenderer—overviewRulerオプション未使用。かつ canvas は viewport の前に兄弟として挿入されるので、隠しても消えない。DomRenderer—_viewportElement.style.height(インラインスタイル)を読む。CSS のdisplayはインラインスタイルを書き換えないため、前後で読み値は変わらない。- ホイールスクロール — viewport にリスナは付いていない。
handleMouseWheelは scrollable element 側。
受け入れ基準
- 測定してから直している — 当て推量の CSS ではない。#68 で 6.63px → 14.44px(800px 窓)へ動いたという数字まで出ており、「#68 以降に見え始めた」という観察と符合する。
- 端末の面積を減らしていない(#77 の制約)—
.termは 980.44×359.44 のまま。 - 補正の仕組みを壊していない —
fitShownの呼び出し口は無改変。resize_ptyの桁数と行数は描かれている面と一致(126×7.617=960、23×15=345)。 - 三軸の独立(#60 / #66 / #68)は無改変。
ついでの掃除
height: 100% のセレクタから .xterm-viewport と .xterm-screen が落ちている。どちらも効いていなかったという理由が書かれており(screen は renderer のインライン height を持つためスタイルシートが届かない、viewport は四辺に位置指定済み)、妥当。
実機未確認
- WebView2 での見え方。実測はブラウザで行っており、実機の webview ではない(docs に記録済み)。
- scrollback のホイールスクロール。 経路上は壊れないことを上記で確認したが、動きそのものは未確認。実機で端末を流したときに追随するかを見ておくのが安全。
次の一手
実行モード auto。この自己レビュー通過をもって squash merge する。リリース種別は patch 相当(観測可能だが、CSS 一行の修正であり構造変更ではない)。
Closes #77
端末の下辺に出ていた横方向の帯と、右端のスクロールバーの矢印を消す。原因は実測で特定した。実際の
src/styles.cssとindex.htmlの DOM からブラウザ上に採寸用のページを組み、そこで箱を測っている。原因は
.xterm-viewportである。xterm 6 のスクロールは.xterm-scrollable-elementが持ち、この要素は誰も測らない空のdivとして残っているが、xterm 自身のスタイルシートは保守されていない二つの指定を当てたままである——テーマの届かない#000の塗りと、ネイティブのスクロールバーを 15px 分確保するoverflow-y: scrollである。端末の箱はheight: 100%で面いっぱいへ伸ばしてあり、xterm が描くのは行の整数倍であるから、行数 × 行の高さを引いた余りが下辺に残る。そこをこの空の要素が黒で塗っていた。帯は割り算の余りであり、面の高さとともに動く(実測、600〜900px の窓で 4.44px 〜 14.44px)。新しく生まれたものではなく、#68 が 800px の窓で 6.63px から 14.44px へ動かした。
前提に書かれていた仮説(
.xterm-screenが.xterm-viewportより広くなって横のバーが出る)は実測で否定された。.xterm-screenは.xterm-scrollable-elementの中にあり、横方向の溢れは無い。補正も壊れていない——fitが測るのは.termであり、桁数と行数は実際に描かれている面と一致する(修正の前後どちらでも 1280×800 の窓に対して 126×23、画面は 960×345)。制約の確認:
.termは 980.44×359.44 のまま)。fitShownの呼び出し口には触れていない。三軸の独立(feat(ui): let the whole UI be scaled from a global setting #66 / feat(ui): give the terminal pane a header with tabs, close and size #68 / feat(ui): let the chat text size be changed #60)にも触れていない。resize_ptyへ渡す桁数と行数は実際に描かれている面と一致する(126×7.617=960 / 23×15=345、いずれも画面の実寸と一致)。未確認: WebView2 上の見た目。採寸はブラウザ上の計測ページで行っている。