Repository navigation
[deps] フォーク依存の棚卸し:zed / gpui-kit / cosmic-text / gpui-terminal は何を直しているのか、どれを捨てられるのか #1479
Description
現在の追跡先・受入条件(v0.45.0後のIssue整理)
#436 の依存移行は本Issueの S6 に統合し、#466 の旧中間revision案は採用しない。以下の元本文は2026-10-13時点の調査記録として保存する。現行方針は後続のオーナー承認と upstream-migration-1479.md に従い、公式Kit revision+registry gpui-pre、fork/patch追加なし。v0.45はS0〜S5の準備までであり、本Issue/S6は未完了。
#436から引き継ぐS6受入(元Issueを閉じても未完了のまま維持)
- root・各UI crate・web・terminalを含む全manifestが単一GPUI供給元で、active dependency closureに重複GPUI/fork source/patchがない。component/base/assetsは同一の採用公式revisionに揃える。
- 旧InputStateの複数行/コード用途をTextarea/Editorへ移行し、公開Find/Radio/Select/theme APIを使用。日本語コミット入力・IMEを実機で確認し、人間の受入結果を記録する。
- gpui_kit_assetsへの移行と実際のicon描画を確認。
- Dark/LightのThemeColor/tokens同期とsyntax highlightingが崩れないことを実画面で確認。
- build/workspace testsと関連nativeを通す。kagi-web buildと既存Playwright 2 spec、移行計画にあるLinux/Windows/WASM/AX等のgateを省略しない。
- ADR-0001/0006等に公式供給元・revisionを選んだ根拠とgpui-pre採用判断を残す。古いrev-less/リポジトリ名/patchコメントを更新し、research auditの旧0.5.1前提を明示または更新。
- Markdown固有の移行・画像・table・Mermaid・選択copy・sanitizer検証は [deps][Markdown] 公式GPUI Kit移行時の描画・選択・sanitizer受入を保持する(#466後続) #1634 の全checklistと合わせて受け入れる。#466のGPLゲートと製品要件は失効しない。
関連するcapture lifecycleの残検証は #1632。#436/#466のcloseは重複/旧計画の整理であり、S6完了やv0.45での公式依存切替を意味しない。
1. 目的
Kagi の依存関係には、自前のフォーク(独自の修正を当てた複製)が4つあります。これらの修正は主に AI が書いたもので、人間はその中身を把握できていません。フォークが増えるほど本家(upstream)の更新に追従できなくなるため、なるべく減らしたいと考えています。
この Issue では、各フォークについて次の4点を一次資料(git の差分、upstream のコード、Kagi 内の呼び出し箇所)から確かめた結果をまとめます。
- 何の問題を直しているのか
- Kagi のどこがそれに依存しているのか
- upstream ではすでに直っているのか
- 使い方を変えればフォーク自体が要らないのではないか、upstream に PR を出せば取り込まれそうか
調査日は 2026-10-13 です。この調査では、Kagi 本体のコードを一切変更していません。
2. 結論(先に要点)
| フォーク | 中身 | 捨てられるか | 次の一手 |
|---|---|---|---|
| TomiXRM/cosmic-text | 1コミット。ja-JP のような地域付きのロケールを判定できず、漢字が中国語(簡体字)の字形で描かれる不具合を直す |
ほぼ捨てられる(Linux のみの問題で、Kagi 側の設定で回避できる) | 端末の描画にフォールバックフォントを明示してから patch を削除する |
| TomiXRM/zed | 1コミット、gpui/src/elements/list.rs だけ。可変高リストで、未計測の行の推定高さを保持する。末尾までスクロールしたい意図も扱う |
今は捨てられない | upstream に2つに分けて提案する。ADR-0222 の事実誤認を直す |
| TomiXRM/gpui-kit(gpui-component) | 12コミット・+4361/−330 行。2026-10-10〜13 の4日間で積まれた | 今は捨てられない。半分ほどは upstream の最新版ですでに解決済み | #436 の移行を最優先する。小さな修正3件は upstream に PR を出す |
| vendor/gpui-terminal | 選択とコピー、端末からの問い合わせへの応答、マウス報告、貼り付けの安全化など。実質的には Kagi が自分で持っている端末部品 | 捨てられない(upstream はほぼ停止している) | 「一時的なフォーク」という扱いをやめ、Kagi のコードとして文書化する |
いちばん大きな問題は個々のパッチではありません。進め方そのものです。
- 2026-09-03 に [deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436 で「gpui-component の基準コミット
b004e595は upstream より 254 コミット遅れている。追従が必要」とすでに指摘されていました。 - にもかかわらず、その後 [deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436 には手を付けず、その古い
b004e595の上に 2026-10-10〜13 の4日間で12コミットの独自修正を積みました。 - 遅れは 628 コミット に広がっています。upstream は crate 構成と GPUI の取得元(
gpui-pre =0.3.9)まで変わっています。 - 独自修正のうち upstream に PR を出したのは1件(text: Preserve logical selection across presentation changes longbridge/gpui-kit#3421、マージ済み)だけ です。残り11件には PR がありません。
AI に1件ずつ課題を直させると、「upstream に合わせる」より「手元のフォークに足す」ほうが毎回安く見えるため、フォークは膨らみ続けます。これは方針として止める必要があります(§7 の提案)。
3. cosmic-text フォーク
宣言箇所:Cargo.toml の [patch.crates-io] cosmic-text = { git = "https://github.com/TomiXRM/cosmic-text", branch = "fix-han-unification-0.19" }(コミット 7c9c36a8)
何を直しているか
- upstream のタグ
0.19.0に1コミットを足したものです。対象はsrc/font/fallback/unix.rsのhan_unification()で、+37/−5 行とテストの追加です。 - 不具合:cosmic-text は OS のロケールを
"ja-JP"の形で受け取ります。ところが判定は"ja"に完全一致するかどうかだけでした。そのため日本語や韓国語の環境が、すべて既定の「Noto Sans CJK SC(簡体字)」に流れていました。結果として、かなは日本語の字形、漢字は中国語の字形という混在が起きます。 - 修正内容は、ロケールを
- _ . @で区切り、言語の部分(と中国語の地域)で判定することです。
upstream の状況
- PR:Fix Han unification fallback ignoring locales with region subtags (ja-JP got SC glyphs) pop-os/cosmic-text#522(TomiXRM 作、2026-07-08 から open のまま、レビューなし)
- upstream main も同じ不具合のままです。最新リリースも 0.19.0 のままです。
実は Linux でしか効いていない
- cosmic-text は
gpui_wgpu経由でしか使われず、gpui_wgpuを使うのは Linux/FreeBSD だけです。macOS は CoreText、Windows は DirectWrite を使います。macOS 版にとって、このパッチは何もしていません。
使い方の問題ではないか → 大部分はそうです
- ADR-0130 で Noto Sans JP を同梱し、
crates/kagi-ui-core/src/fonts.rsのui_font()がFontFallbacks(["Noto Sans JP"])を Root に設定しています。そのため、通常の UI やコード表示は cosmic の自動判定(不具合のある経路)にそもそも到達しません。 - 残っている経路は端末です。
vendor/gpui-terminal/src/render.rs:247と:633がfallbacks: Noneのフォントを作っているため、端末に表示する日本語だけが不具合のある経路を通ります(この2行は確認済み)。 - さらに、パッチがあっても cosmic が選ぶのは OS に入っている「Noto Sans CJK JP」です。最小構成の環境や AppImage など、そのフォントが入っていない環境では、パッチは何の役にも立ちません。
提案
TerminalConfigにフォールバックフォントの項目を足し、render.rsの2か所にNoto Sans JPを渡す。- Ubuntu の
ja_JP環境(fonts-noto-cjkあり)で、端末・差分・コード表示の「直・骨・誤」が日本語の字形になることを確かめる。 - 確かめられたら
[patch.crates-io] cosmic-textを削除する。PR Backend::run: CreateBranchWithCheckout の途中失敗が oplog 記録を迂回する #522 は放置せず、催促するか取り下げるかを決める。
確度:中〜高です。端末以外に「フォールバック指定なしで文字を描く経路」が残っていないかは、網羅的には調べていません。
4. zed(gpui)フォーク
宣言箇所:Cargo.toml の [patch."https://github.com/zed-industries/zed"](22 crate すべてを TomiXRM/zed@caf50076 に向けている)。関連文書:ADR-0222、docs/decisions.md の 2026-10-10 の行
何を直しているか
- upstream の
90b3aa0(2026-07-13)に1コミットを足したものです。変更はcrates/gpui/src/elements/list.rsの1ファイルだけで、+380/−42 行(うち約260行はテスト)です。 - 他の21 crate は中身が upstream と同一です。 取得元を揃えるためだけに fork に向けています。
- 新しい公開 API はありません。 upstream にすでにある
ListState::with_uniform_item_heightとreset_with_uniform_heightの振る舞いを変えています。- 推定の高さが、
spliceやreset、幅の変更で失われないようにする。upstream では一度設定すると、これらの操作で消えます。 - 同じ値で再び呼んだときは何もしない。upstream では毎回全行を作り直し、計測結果がすべて捨てられます。
- 推定した末尾まで一気にスクロールしたら、本当の最後の行に揃える(end intent)。
- 幅が変わってもスクロール位置を保つ。
- 推定の高さが、
ADR の事実誤認
- ADR-0222 と decisions.md の 2026-10-10 の行は「upstream の
listは estimated-height API を持たない」と書いています。これは誤りです。 基準コミット90b3aa0にはwith_uniform_item_height、reset_with_uniform_height、measure_allがすでにあります(確認済み)。 - 正しくは「upstream にも推定高さの指定はあるが、
splice、reset、幅の変更で失われる」です。この誤りは AI が書いた ADR をそのまま採用したことで生まれています。
依存箇所
crates/kagi-ui-core/src/diff_list.rs:58のDiffListLayout::syncは、描画のたびにwith_uniform_item_heightを呼びます。upstream の振る舞いのままだと毎回全計測が捨てられるので、このコードはフォークの「同じ値なら何もしない」に暗黙に頼っています。- ADR は「diff list だけが opt-in する」と書いていますが、これも実態と違います。
with_uniform_item_heightを呼ぶ他のリストもすべて新しい挙動になっています。該当箇所はsrc/ui/pr_lane.rs:237、issue_conversation.rs:105、tab_view.rs:601、issues_mode/feed.rs:181、inspector_files.rs:140、operations/oplog_restore_inspection.rs:82です。
upstream の状況・代替案
- zed main(
cb13b37、2026-10-13)にも同等の修正はありません。TomiXRM からの PR もありません。 - 代替案1:
ListState::measure_all()。 最初のフレームで全行を計測します。スクロールバーと末尾が正確になり、フォークは不要になります。代わりに、差分の全行で文字の配置計算が走ります。行数が数千程度なら許容範囲と思われますが、1万行を超えると1回の計測に数百ミリ秒〜数秒かかると見積もっています(実測はしていません)。「行数が少なければmeasure_all、多ければ別の方法」という組み合わせは検討に値します。 - 代替案2:
scroll_to_end()とis_scrolled_to_end()を使い、末尾に揃える処理を Kagi 側でまねる。ただし推定高さが失われる問題は残ります。
提案
- 今は維持します(確度:高)。外すと [HIG][Diff] 大規模diffで未測定行がheight=0扱いになり、scrollbarが嘘をつき末尾へ到達できない #1122 の退行が戻ります。
- upstream には2つに分けて提案します。(a)「推定高さが splice/reset/幅変更でも保たれる・同じ値の再設定では何もしない」は既存 API の不具合修正として出しやすいはずです。(b) end intent はスクロールの意味を変えるので、議論が必要です。zed の CONTRIBUTING は「機能追加はコードを書く前に議論を」と求めているので、まず Discussion を立てます。取り込まれる見込みは、分割すれば五分五分程度です(低確度の推測)。
- ADR-0222 と decisions.md の誤りを訂正します。
- フォークの差分は list.rs だけで、upstream 側の list.rs の変更とも重ならないため、rebase のコストは低いです。
5. gpui-kit(gpui-component)フォーク
宣言箇所:Cargo.toml の [patch."https://github.com/longbridge/gpui-component"](TomiXRM/gpui-kit@ee625584、fork 側の branch は kagi/undo-group)
- 基準は
b004e595(2026-07-13)で、その上に12コミットを直線的に積んでいます。すべて 2026-10-10〜13 のものです。 - upstream main(
8d4907d、2026-10-13)はそこから 628 コミット先です。crate 構成はbase/component/kitに分かれ、GPUI の取得元は crates.io のgpui-pre =0.3.9に変わっています(確認済み)。 Cargo.toml:174付近のコメント「gpui-component の fork patches は dropped」は現状と食い違っており、読む人を誤らせます。
| コミット | 直している問題 | Kagi の依存箇所 | upstream main の状況 | 判断 |
|---|---|---|---|---|
941f20e6(+3.4k 行), ae37bfd4 |
仮想リストで画面外に外れた投稿も含めて、まとめて選択・コピーできるようにする(Root::set_text_selection_group など) |
src/ui/issue_conversation.rs、src/ui/issues_thread.rs(ADR-0198) |
一部だけ upstream に入りました(#3421、マージ済み)。ただし #3421 自身が「外側の仮想リストで外れた行は扱わない」と明記しています。upstream には新しい選択の仕組みがありますが、これで足りるかは未検証です | 当面は維持。最も重い部分です。upstream の新しい仕組みの上で作り直すか、機能そのものを見直す必要があります |
f7e696f6 |
InputState::wrap_row_count():折り返し後の行数を公開する |
src/ui/commit_panel_render.rs:405 |
公開されていません | upstream に PR(小さく汎用的) |
b50a0796 |
入力欄が次フレームの処理で自分自身を強く保持し、メモリが解放されない | 挙動のみ(pr_detail_lifecycle のリーク検査) |
解決済み(仕組みごと作り直されています) | 移行すれば不要 |
13386100, 879f3bfa |
ラジオボタンの選択表示がチェックマークになっていたのを、中央の丸に変える | src/ui/settings_view.rs(見た目のみ) |
直っていません | upstream に PR。見た目の好みと見なされて断られる可能性はあります |
3dad9fcc, ac2f8ceb, f5f68275 の一部 |
検索バーのボタンに読み上げ名とツールチップがない。日本語化できない。Tab や Enter が裏のエディタに漏れる | crates/kagi-ui-core/src/find.rs、kagi-ui-editor、conflict_view.rs、issues_composer.rs |
直っていません(upstream には日本語訳もありません) | upstream に PR(汎用的な部分)。テスト用の覗き見用フックはフォークに残すか、設計し直す |
406b02f2 |
Select の読み上げ情報を、実際にフォーカスを持つ部品に付ける | src/ui/settings_view.rs:487,501-504 |
別の API で解決済み(accessibility_label など) |
移行時に呼び出し名を変えれば不要 |
a97efac0, f5f68275 |
Input::aria_label:入力欄に読み上げ名を付ける |
約18か所 | 解決済み(Input::aria_label が upstream にあります。確認済み) |
移行すれば不要。テスト専用の補助関数は代わりが必要 |
ee625584 |
文面の全置換(Smart 生成)が直前の入力と同じ取り消し単位にまとまり、元に戻すと手入力まで消える | src/ui/commit_panel_composer.rs ほか |
解決済み(取り消し管理が作り直されています) | 移行すれば不要 |
| (Cargo.toml) | test-support feature |
gui-e2e feature |
upstream にあります | 移行すれば不要 |
まとめ
- 12コミットのうち、約半分は upstream main ですでに解決済みです。これらのフォークは「古い基準に留まっているから必要になっている」だけです。
- 本当に upstream に無いものは4つです。仮想リストを横断する選択、
wrap_row_count、ラジオの丸、検索バーの読み上げです。 b004e595に戻してフォークを外すことはできません(どのコミット群にも Kagi のコードが依存しています)。- フォークを減らす現実的な道は、[deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436(upstream main への移行)を進めることだけです。移行すると GPUI の取得元が crates.io の
gpui-preに変わるため、§4 の zed 側の修正は[patch.crates-io]のgpui-pre*向けに移し替える必要があります。
6. vendor/gpui-terminal
宣言箇所:Cargo.toml:51 の path 依存(ADR-0035)。元は crates.io の gpui-terminal 0.1.0(zortax/gpui-terminal)
Kagi の変更内容
crates.io 版との差分で確認しました。
- マウスでの選択と Cmd+C でのコピー(ADR-0035 が挙げている理由)
- 端末からの問い合わせへの応答(PtyWrite)。zellij が起動時に止まる問題を直します。
process_bytesの戻り値が変わるAPI 変更です - マウス操作を端末アプリに伝える機能(SGR mouse reporting)と、スクロールの振り分け
- Cmd を押しながらのキー入力で文字が漏れる問題の修正
- 貼り付けの安全化(改行や制御文字を含む貼り付けの確認)
- 新しい gpui への追従(crates.io の gpui 0.2.2 から zed の git 版へ)
- upstream にあったテストの一部を削除
upstream の状況
- リリースは 0.1.0(2025-12)のみで、最終コミットは 2026-01-11 です。他の人の PR も止まっています。実質的に止まっています。
- crates.io の gpui を前提にしているため、Kagi の変更をそのまま返すこともできません。
判断
- ADR-0035 が言う「upstream が選択とコピーを実装したら外す」という条件は、満たされる見込みがありません。
- これはフォークではなく Kagi のコードです。 ADR-0035 を「一時的な vendor」から「Kagi が所有する端末部品」へ改め、保守の責任を明確にすべきです。
- Zed の terminal crate は GPL なので、代わりには使えません(ADR-0008)。
alacritty_terminalを直接使う案は、今の状態とほぼ同じ作業量になります。
7. 提案(優先順)
- [方針] フォークへの新規コミットを原則止める。 gpui-kit や zed のフォークに足すときは、同時に upstream への PR(または Discussion)を出すことを条件にします。出せない理由があれば
docs/decisions.mdに書きます。これを CLAUDE.md の不変条件に加え、AI が「フォークに足すほうが安い」を選び続ける構造を断ちます。 - [文書]
Cargo.tomlの patch ごとに、「何を直しているか・upstream の PR/Issue・外す条件」のコメントを付ける。 誤っている「gpui-component fork patches were dropped」は削除します。ADR-0222 の「upstream に API が無い」を訂正します。 - [すぐ外せる] cosmic-text:§3 の手順で端末にフォールバックを足し、patch を削除します。
- [upstream PR・小] gpui-kit 向けに
wrap_row_count、ラジオの丸、検索バーの読み上げ。ただし upstream main(新しい crate 構成)に向けて出す必要があります。b004基準のコードはそのままでは当たりません。 - [upstream 提案] zed の list 修正を2つに分けて Discussion を立てます。
- [本丸] [deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436 を再開します。 これ以上
b004の上に積むと、移行のコストは毎日増えます。移行で gpui-kit フォークの約半分が消えます。 - [文書] gpui-terminal:ADR-0035 を「Kagi 所有」に改めます。
8. 未確認・判断できなかった点
- upstream gpui-kit の新しい選択の仕組みで、仮想リストの画面外の行のコピーを実現できるか(#3421 の記述からは「できない」と読めますが、コードでは確かめていません)。
measure_allを Kagi の大きな差分に使ったときの実際の速度(未計測で、上の数字は推定です)。- 端末以外に、フォールバック指定なしで日本語を描く経路が残っていないか(網羅していません)。
- ラジオの丸の変更が upstream に「不具合修正」と認められるか。
- 検索バーのキーボード操作のうち、upstream の
cycle_focusでどこまで足りるか。
9. 一次資料
Cargo.toml(51行目、167〜205行目)、Cargo.lock- フォーク:TomiXRM/zed@caf5007 / TomiXRM/gpui-kit
b004e595..ee625584/ TomiXRM/cosmic-textfix-han-unification-0.19(7c9c36a8) - upstream PR:Fix Han unification fallback ignoring locales with region subtags (ja-JP got SC glyphs) pop-os/cosmic-text#522(open)、text: Preserve logical selection across presentation changes longbridge/gpui-kit#3421(マージ済み)、Support terminal scrolling zortax/gpui-terminal#2(open)・v0.3.5 — performance, working-tree WIP, Windows + auto-update, conflict & UI fixes #3(closed)
- ADR-0035、ADR-0130、ADR-0198、ADR-0222、
docs/decisions.md(行 23, 89–93, 247, 249, 301, 305, 423) - 関連 Issue:[deps] gpui-component v0.6.0 / GPUI Kit 改称への追従(gpui-pre 0.3.x への供給元切り替えを含む) #436
Activity
TomiXRM commentedon Oct 14, 2026
#1479 フォーク依存の再検証と #436 移行判断
調査基準日: 2026-10-15。調査のみ。Kagi 本体・worktree の編集、commit、push、PR、Issue コメントは行っていない。Kagi は
origin/dev = 91d2d10570cd88fc8e43151c8bb0a63003bb2a4aを/tmp/kagi-fork-review/kagiに読み取り用に固定し、GitHub の dev tip と一致することも確認した。以下のKagi:行はこの snapshot の行番号であり、#1479 に記載された古い行番号とは異なる。一次資料: #1479、#436、Kagi 基準、GPUI Kit main 基準。GitHub の旧 URL
longbridge/gpui-componentはlongbridge/gpui-kitへの redirect。本報告の「可能」は、依存解決の実験で確認した事項と、公開 API を読んで成立すると判断した設計を分けている。Kagi 全体を移行して build/E2E を完了した意味ではない。コード片は置換設計の sketch であり、未実装の Kagi 機能を実装済みとは扱わない。
結論表
zero_patch_feasible = trueの意味: dependency解決自体は実験で確認済み。製品側に不足機能を正しく所有させる設計なら全[patch]を除去できる。ただし 現行コードのpatch行だけを削除して使える、すべての外部UI widgetをそのまま残せる、Kagi移行build/E2Eに合格した、という意味ではない。最も重要な制約は、現行の完全な投稿選択契約・公式TextView無改造・独立した外側投稿virtualizationを全部維持する組合せには公開extension seamが足りないこと。その組合せを必須にするならupstream変更がblockerになる。zero-patch案は、単なるwrapperではなくKagi-owned rich renderer/document adapterへ責務を移す(又は上流seamを得る)ことを明示的に含む。cross-post選択を黙って落とす案ではない。
#1479 のclaim再判定一覧
「確認」は現在の一次資料と一致、「変更」は当時のsnapshotから変化、「誤り」は現在のコードで成立しない、数値的な採用確率等は「根拠なし」とする。詳細なcommit/API/行番号は後続節。
fork ごとの判定(commit 単位)
A. TomiXRM/cosmic-text —
7c9c36a8f87227902d379c59100798c0cb015946判定: fork は不要にできる設計だが、端末2行だけでは撤去条件を満たさない。 本体のstatus幅計算と描画外テスト計測もfontを統一する。Linuxでの字形実証は本調査では行っておらず、撤去PRの受入条件である。
公式mainは
f1a3461f3e8df67d2bbcfafb8bc61dc7aee5b2a6、fork branch headは7c9c36a8、親は0.19.0c24886c2471e5606587c46090cd25dbbf209186b。唯一のfork commitを以下で判定する。PR #522 はstate OPEN、mergedAt null、reviews[]、comments[]、updatedAt2026-07-08T07:00:54Zを今回再取得した。#1479のclaim再検証
明示fallbackによる正確な置換箇所
既存vendorを保守する場合の最小一貫変更は:
FontFallbacksはArc保持なのでglyph hot pathでVec/Stringを毎回構築しない。Kagi側からNoto Sans JPのexisting共通定義を使い、generic rendererへKagi固有literalを埋め込まない。
更新対象:
view.rs:141-183、renderer field/newrender.rs:170-229、view newview.rs:505-510、live update:1208-1220、Font両箇所:247,:633。src/ui/terminal.rs:111-132。commands.rs:2082-2114がall retained session + other windowsへconfigをcloneして渡すので、この既存pipelineを維持。zoom commands :1961,1968,1975、theme :2140、theme_select.rs:199、settings_view.rs:553,563は原則API追加不要、live反映テストが必要。注意: ownerの『official crates as-is』を厳密に守るなら、vendorへfieldを増やす案は最終方針に合わない。これは現vendor修正の必要配線説明であり、公式gpui-terminal無変更でこの機能が使えるという提案ではない。
B. TomiXRM/zed —
caf5007618c309a8ea66002d44a9c9ab629a2d35判定: upstream の同等修正はまだ無い。ただし fork を永続維持・別 package 名へ移植することが唯一の道ではない。 小規模は公式 ListState の使用法修正、大規模は Kagi が公開 Element API 上にリスト geometry を所有する方針を推奨する。単純な2方式切替だけでは不足する(後述)。
90b3aa0b3bd3b453775b11a386907c7ac9acd997。fork branch head は現在も caf50076。1 commit、crates/gpui/src/elements/list.rsだけ +380/−42、追加 test 257行。22 patch package のうち他21 crateに独自実装差分はない。with_uniform_item_height/reset_with_uniform_height/measure_allは基準 upstream に既存。ADR-0222:9 の「APIが無い」は誤り。set_offset_from_scrollbarは独自差分にない。Tail modeを使うと継続追従へ意味が変わる。PendingScroll::Absolute。明示 remeasure / estimate変更の Proportional と同一ではない。ADRの幅変更一般を fractional とする記述も訂正が必要。c403bc6af644cd0ab60c90045ac6dc3436218809の list.rs と registry gpui-pre 0.3.9 の list.rs は byte-identical(SHA25642704328bf8f1f5c75b0129dfaf4052e475f2de1bcfec3a2c6f92f518c6cb573)。main 全体と registry 全体が同じという意味ではない。基準→mainの差分は +192/−8、follow pause / unconstrained width / nested scroll priority 等で、上の独自挙動は未採用。gh pr list --repo zed-industries/zed --author TomiXRM --state all --limit 100は空。PR採用可能性を「五分五分」と数値化する根拠はない。依存箇所:
crates/kagi-ui-core/src/diff_list.rs:47–81、特に renderごとのwith_uniform_item_height:71、reset :73、gutter remeasure :75、source/split reset :78。共通接続はsrc/ui/render_helpers.rs:606–612,674–721,791–806。所有者はmain_diff_pane.rs:42–43,file_history.rs:34–35,pr_mode.rs:71–72,101–102,kagi-ui-editor/src/lib.rs:466–467,539–540。可変高threadはrender_helpers/row_overlay.rs:86–104の splice。非diffの6箇所も対象:
pr_lane.rs:237,issue_conversation.rs:105(更新splice :301–304),tab_view.rs:601,issues_mode/feed.rs:186(splice/remeasure :177–191),inspector_files.rs:140(splice/reset :93–95),operations/oplog_restore_inspection.rs:82。ADR「diffだけopt-in」は誤り。独自commit / 公式 list.rs / registry source。
C. TomiXRM/gpui-kit — 現在13コミット、単純な「約半分不要」では足りない
Kagi pinと
kagi/undo-group現headはともに7a116a2019160e6d8091075f88f349a9fe9a2a7e。基準b004e595cf5de98a73b6b561394a559a94ae1e2aから 13 commits / 34 files / 最終差分 +4384/−338(GitHub固定compare APIのfiles集計)。#1479の12 commits、ee625584、+4361/−330は過去snapshot。現在upstream mainは1188c110で 645 commits ahead(git rev-list実測)、旧628から増えた。以下で K = Kagi91d2d105、F = fork7a116a20、U = official main1188c110。Uのlocal rootは
/tmp/kagi-fork-review/component/。default fork branchとKagiが使うpatch branchを混同していない。#3421 はmerged、2026-10-09 13:51:27Z、squash
0bbd9870ce420af9feacbf948b21d3824d82dc0b。TomiXRM作者のupstream PR searchはtotal_count:1, incomplete_results:falseでこれだけ。fork選択機構全体がmergeされたわけではなくpresentation retentionの一部だけ。PR本文のouter-unmount非対応はコードとも一致する。全13コミットの台帳
+/-は各 commit patch の diffstat(最終 aggregate ではない)。リンクは full SHA の一次資料。InputState::wrap_row_count()公開。再shape/allocateしない。display_map().wrap_row_count()。Textareaには direct cached count getterなし、auto_growあり。window.on_next_frameclosure のEntitystrong clone を weak にし、removed input を callback が保持しない。theme.primary_foregroundに修正。13 commit diffstatの単純合計は +4404/-358(ネット+4046)。この合計には同じ行の再編集が含まれるため、#1479の最終 diff +4361/-330と直接比較して『不正確』とはいえない。今回確認できた固定 compare の最終 file数は34。日付もUTCなら10/9〜14、patch作者のJSTなら10/10〜14で、旧12コミット/10/10〜13は当時の記録。
2. 正確な production caller 棚卸し
A. 論理投稿選択
src/ui/issue_conversation.rs:121:Root::set_text_selection_group(None)。:140:Root::set_text_selection_group(Some((group,&members)))。:40-95:TextSelectionGroupRetirementfield、retire()1 invocation、is_active1 method reference。Drop/activation所有権を結ぶ。:277-284:全 accepted posts の members を発行。外側 listで描かれた postsだけではない。src/ui/issues_thread.rs:136:text_selection_groupviewport marker 1箇所、callbackが同じowner.listをscroll。gpui::list(同:98-134)。今回の対象は Issues thread。PR会話にこのgroup APIが直接使われると主張しない。docs/adr/0198-read-only-issues-workspace.md:88-159がcanonical rendered UTF-8、stable ID/source revisions、native geometry retirement、already-focused offscreen SelectAll契約を記録。B. wrapped height
src/ui/commit_panel_composer_render.rs:33、.wrap_row_count().clamp(3,12)。tests/recovery/commit_panel_viewport.rs:188。commit_panel_render.rs:405ではなく、現在は上記の抽出済みファイル。C. Radio
Settingsの 4 RadioGroup:
src/ui/settings_view.rs:699graph copy target、:713toolbar表示、:750accent source、:965language。直接Radio::newは0で、RadioGroupのchildrenが内部Radioに変換される。D. Find/Replace labels
search_labels3+set_search_labels3 = 6 code sites/3 surfaces。src/ui/conflict_view.rs:1094,1020。src/ui/issues_composer.rs:81,185。crates/kagi-ui-editor/src/lib.rs:1539,1592。crates/kagi-ui-core/src/find.rs:3-19。名前9つ=5 icon controls(previous/next/close/case/mode)+2 text controls(replace/all)+2 input fields。tests/recovery/editor_find.rs:26search_panel_probe、:73clear_search_help_probe。Button.bounds_probe/tooltip_builderはKagiが直接呼ぶのではなくfork SearchPanel内部が利用。E. Select
constructor1:
src/ui/settings_view.rs:516。:517-520のrole/name/value/expandedを実triggerへ設定。:503のis_open getterもfork API。Uではこれらをcallerで重複管理する必要なし。F. named Inputs
production Input construction18 sites、全て
.aria_label(...)(17 root UI+1 editor crate)。共有helperは多数のruntime fieldsを表すため18は『画面上の入力欄総数』ではない。src/ui/command_palette.rs:487-488;commit_panel_composer_render.rs:46-47,87-88;conflict_editor.rs:1567-1568;home_github_list.rs:414-415;issues_composer.rs:401-402;list_filter_strip.rs:367-368;modal_renderers_editor_fs.rs:92;modal_renderers_input.rs:38,77,353;modal_renderers_stash.rs:163-164;pr_fields.rs:352;remote_browse_render.rs:36;settings_view.rs:1139-1140;sidebar_panes.rs:511-512;timeline_row.rs:202-203;crates/kagi-ui-editor/src/lib.rs:3300-3301。fork test seams:
tests/recovery/input_names.rs:33,36,112(drain inventory3 calls)、:66,100(renderer name2);sidebar_filter.rs:33,124(2);sidebar_search.rs:125(1)。take_rendered_inputs_for_testはtest3 calls、rendered_aria_labelはtest5 calls。これらはnative AXそのものではなくrenderer記録。G. Undo replacement
production2:
src/ui/commit_panel_composer.rs:99,113(title/body)。追加test/E2E source:
src/ui/issues_composer_e2e.rs:235,255,290(3);tests/recovery/blame_identity.rs:106,428,429(3);smart_generation_commit_boundary.rs:91,92(2)。これらは通常の Input::replace_all 呼び出しであり、新しいfork専用呼び出し名ではない。H. Clear/IME
src/ui/sidebar_panes.rs:517clear_button_aria_label1。same render_filterがwide/compactに共有される。Kagi policy:
sidebar_focus.rs:196-218(IMEならunmarkのみ、非空ならclear focus保持、空ならlistへ)とmodal_key_routing.rs:254-269(CloseMainDiff fallbackがfocused sidebarを判定)、bindingcommands.rs:1387-1390。3. 公式 API の同等性と具体的 no-fork 置換
3.1 Input AX:同名APIあり、test hooksは公式観測へ
U
crates/component/src/input/input.rs:271-274にInput::aria_labelあり。:734-779は実BaseInputへname/role/value/focus actionを付ける。source。baseinput/base/mod.rs:143の実controlは.test_support()登録済み。置換:単行はInput/InputState、bodyはTextarea/TextareaState、code editorはEditor/EditorStateへ型を移す。その後それぞれ
.aria_label(Msg::...t())を維持。fork renderer getter/weak inventoryは削除し、公式TestQueryExt::find_all_by_role、find_by_label、TestWindowExt::within/findからElementSnapshot::{label,role,focused,value,bounds}を読む。U
crates/base/src/test_support.rs:17-116、crates/kit/src/test.rs:31-79、crates/kit/src/test/query.rs:5-22は公開API。test-supportはcomponent Cargo.toml:19/kit:27に存在。local GPUI要素は.test_support()を付ければ同じ観測方式。限界:Snapshotはframeworkが読んだAX propertiesであり、実OS VoiceOver発話の記録ではない。既存native_ax::press/treeシナリオは残す。help/tooltip文言はSnapshotに汎用getterがないので既存tooltip表示・native AX helpかKagi側観測で確認する。f5のname=placeholder suppressionはU
input.rs:734-766に同等条件がなく、placeholderも別にemitする。『a97/f5の全てが同一実装で解決』は誤り。3.2 Select AX:labelは改名、role/value/openは自動
U component
select.rs:691.accessibility_label。render:849-862はstateからopen/valueを取り、BaseSelectへ渡す。baseselect.rs:196-207が実focus rootでComboBox/expanded/label/valueを公開する。source。[INFERENCE]
Select::new(&state).accessibility_label(Msg::SettingsTheme.t()).menu_width(...).small().w(...)に置換し、.role/.aria_value/.aria_expandedとcallerのis_open計算を消す。Kagi Theme SelectItem::titleは既にlocalizedなので自動valueを使える。ただし現callerはcontrol-byte sanitationを明示している。custom theme由来titleをSelectItem境界で同じsanitizeに通すことは維持する。公式がsanitizeするという主張はしない。3.3 Undo:同名 replace_all のまま Atomic で前後を隔離
単なる『取り消し管理が作り直された』以上に、挙動を追跡した。
replace_all → replace_text。:1139-1151はprogrammatic full-range replacementに明示的EditIntent::Atomicをセット。:237のcoalescing_boundary = intent == Atomicがfollowing typingとのcoalesceも防ぐ。source。つまりee625584の『生成前手入力/生成全置換/生成後手入力』の3段Undoを分離する意味は公式で満たす。title/bodyは別state別managerなので独立性も保持。置換callerをset_valueへ変える必要はなく、変えると履歴clear(state.rs:1064)が起きるため誤り。
3.4 wrapped row count:Editor公開とTextarea不足を混同しない
U EditorStateだけ
display_map()がpublic(state.rs:11833-11838)。DisplayMap::wrap_row_count()はdisplay_map.rs:375-376でpublic。したがってEditorではeditor.read(cx).display_map().wrap_row_count()がfork getterと同じcached countで、getter PR不要。しかしKagi Commit bodyは
.multi_line(true)のplain input(commit_panel_composer.rs:173-181)で、自然な新型はTextareaState。EditorのgetterはTextareaにはない。TextareaState::auto_grow(3,12)(U state.rs:11715-11718)はwrapped countを内部計算し、layoutでupdateする(base/element.rs:1813-1817)。[INFERENCE] no-fork優先案:bodyをTextareaState::new(...).auto_grow(3,12)、renderをTextarea::new(...).aria_label(...)にし、手計算body_height/getterを除去。親composerの
flex_shrink(1),min_h,overflow_hiddenを維持し、必要ならauto-grown実boundsを公開on_prepaintで観測してouter preferred heightへ反映する。同等性の境界:現Kagiは『内部auto-grow minが外側composerの縮小を阻害しない』ため、cached rowsから外側高さを計算しInputを
.h_full()にしている(composer_render.rs:28-35,78-91)。auto_growだけでこのshrink契約まで同一と未検証で断言できない。no-fork代案として現在のbodyをEditorState+line_number(false)/searchable(false)に変えてpublic display_mapを使うことはできるが、mode/key behaviorが変わるため推奨drop-inではない。plain Textareaのままcached countを外側へ無追加shapeで公開したいなら、Textarea/MultiLine public measurement seamの小PRが適切。#1479の『全upstreamに公開なし』は現在のEditorには不正確。3.5 Radio dot:styledは未修正、unstyled primitiveは正式な代案
U component radio.rs:235-240 はまだcheckbox_check_iconを使用。source。
U base radio.rs:15-19 は『activation/focus/accessibilityはframework、indicator/label/layout/colorsはapp所有』を明示。Radio::checked/disabled/accessibility_label/on_change/track_focus/set_position、base RadioGroup::axis/childrenがpublic。
[INFERENCE] KagiのSettings用薄いcomponentをbase::Radio+base::RadioGroupで組み、checked時だけrounded inner divを6/14径、primary_foregroundで描く。現在の4 Settings callbacksをそのまま用い、focus ring、disabled opacity、position/sizeと既存keyboard conventionを維持する。dependency forkは要らない。styled Radio自身を全consumerで修正したいならupstream PRにするが、『視覚好みのためfork必須』ではない。
3.6 Search:cycle_focus が解決するのは2入力だけ
U search.rs:229-245:replace_mode && replaceableならsearch↔replaceを交互focus、それ以外はreturn。forward/backwardは同じ。renderはsearch_input focus handleをpanelにもtrack(:310-335)。
U built-in Findは5 icon controls+2 text controlsにhost labels/help APIなし(:353-459)。case/modeのtoggledは既にあるが名前は別。field自身にもhost aria_labelがない。
SearchPanelLabels/search_labels/set_search_labels/probeは全crates literal searchと別pattern searchで未発見。locale ui.ymlはEN/中国語/一部IT等でja:なし。cycle_focusで解決しないもの:7 buttonsを含むforward/backward順、Enterがancestor editorのFind Nextへ漏れない所有、padding/buttonでBackspace/Paste/Undo/Copy等がunderlying editorに届かないこと、actionがdisabled/消滅した後のfocus戻し、localized names/help、field自身のAX name。
[INFERENCE] no-fork案はFind chromeをKagiが持つがmatcherは公式を再利用する。U base/input/editor/search.rs はpublic
set_search_query(:172)、next_search_match(:191)、previous_search_match(:200)、replace_current_search_match(:211)、replace_all_search_matches(:244)、close_search(:186)、search_session(:136)。state.rs:11592-11607は.searchable(false)でもcustom UIが同engineを使えると明記。具体案:editorをsearchable(false)で組み、Kagi-owned panelに2 named Inputs+7 named official Buttonsを置く。query/case change→set_search_query、prev/next→official methods、replace/all→official methods、close→close_search+editor.focus。match positionはsearch_session().matcherから読む。Cmd-F/Cmd-Hをhost actionsへbindし、selected textでquery初期化、reinvokeでfocus/select-allを維持。既存Kagi keyboard navigation/focus trapでpanel内Tab/ShiftTab、Button Return、残余editor actions/padding pointerを所有する。新matcherやraw text replacement実装は不要。
本当にbuilt-in panelを使い続けたい場合は、label/help options、7 controls focus/Return/containmentのupstream PRが必要。custom host chromeを選ぶならPRはfork撤去の前提ではない。
3.7 新Clear/IME Escape:両方 Kagi へ移せる
Clear
U component/input/clear_button.rs:9-15 はClose icon、text/xsmall/tab_stop(false)のみ、名前なし。input.rs:833-841はこのprivate helperを呼ぶだけ。
clear_button_aria_labelはUにない。U Input::suffix(:281)とcleanable(:317)はpublic。
[INFERENCE]
.cleanable(false)にし、query非空時のみnamed Buttonをsuffixに渡す。Buttonは独立したMsg::A11ySidebarClearSearch名、Close icon、text/xsmall/tab_stop(false)、必要なtooltip。on_clickでは同じinput entityをupdateして clean(window,cx); focus(window,cx)。空ならsuffix自体を出さず、disabled/loading条件は現在のbuiltin show条件と合わせる。Input自身のaria_labelをそのまま残す。AXPressはofficial Buttonのaction経路を使う。名前をsearch fieldから代用しない。IME
F commit13は unmark後のreturn1行。queryを巻き戻す機能ではない。U state.rs:2205-2214は依然
unmark_text → clean_on_escapeならclean、そうでなければcx.propagate。source。Kagiのsidebar ancestor guardはmarked rangeを再確認するが、childが先にunmarkしたらguardはもうcompositionを見られない。query-clear/ancestor escapeへの漏れはこのsource順序から説明できる(実dispatchを今回GUIで再現してはいない)。公開traitでexact policyをhost capture可能:U state.rs:4189
impl<M:InputModeKind> gpui::EntityInputHandler for InputBaseState<M>。:4214marked_text_range、:4223unmark_textはtrait methodsとして公開される。unmark_textはmarked=None+undo_manager.commit_transaction、query/focusは触らない。[INFERENCE/compile未実施] inputを含むwrapperに既存Kagiと同じ
.capture_actionを付ける:通常Escapeは止めない。必要なInput surfaceごとにこのpolicyを共通helperで付け、componentの全Input一律patchだった範囲を明示的に移す。Kagiはcapture_actionを既にcommit_panel_composer_render.rs:57,63で使用。これでforkと同じunmark/consumeが可能なので、IMEをPR-only blockerに分類しない。一般ライブラリ修正として1行returnのPRを提案する価値はあるが、Kagi撤去には必須でない。
tests/recovery/sidebar_search.rs:50-101はfirst nonempty Escape、empty exit、marked Unicode Escapeを明示。:105-158はEN/JA Clear native tree/AXPress/focus/empty removal。今回の『open #1548』metadataと、既にコード/changelogへlandしている事実は別:K CHANGELOG.md:10、decisions.md:18は実装済みと記録する。issueは取得時openのまま。D. vendor/gpui-terminal — Kagi-owned adapterとして責任を明確化するが、派生物の事実は残る
推奨: 公式 alacritty_terminal / portable-pty を無改造依存とし、既存のGPUI接続・安全な貼付・選択等をKagiの保守境界として扱う。既存vendorの再利用自体は許諾されるが、単なる改名でforkが消えたとは数えない。 公式gpui-terminalをそのまま戻すと必須機能が欠落する。
公式main
51f0292938876c8da3de03f0139088591e3be518、公開0.1.0のsource45c63e57181d27c260124a81c7e4b68a6b6e57b0、公開日2025-12-24。以下は公式との差分・Kagi内13 scoped commitをすべて記載する。上流commitの停止と、作者の活動停止を同一視しない。公式公開版・main・Kagiの差
公開0.1.0→current mainの一次diff:
zortax/gpui-terminal@45c63e5...51f0292
mainは0.1.0表記のまま、GPUI
0.2.2、alacritty_terminal0.25.1。公開版から2件のrendering commitとREADME updateがある。M→│によるセル計測、1.0 line-height、864行のbox_drawing module、水平span/vertical component描画など。Kagiはこのmain rendererではなく公開版由来M/font-glyph rendererに独自機能を足したもの。差分をすべてKagi独自修正と数えてはいけない。current mainの未実装はview.rs:735-792のmouse選択/scroll TODO、event.rs:147-149でPtyWriteを握り潰す処理、view.rs:821-823のClipboardLoad TODO。Kagiは選択・scroll・PtyWriteを実装したがClipboardLoad TODOはまだ残る(vendor view.rs:1099-1101)。appのbuild_terminal_viewはresize callbackしか設定せずOSC clipboard_store callbackも設定していない(src/ui/terminal.rs:539-551)。READMEの『OSC52 clipboard integration』を全面対応の証拠にしない。
API差:
TerminalState::process_bytes(&mut self, &[u8]) -> Vec<u8>(vendor terminal.rs:227)、officialは()->()。GpuiEventProxy::pty_responses_handle API追加(event.rs:150)。reader taskはreturned bytesを書戻してからnotify(view.rs:538-553)。PR/activity:
6. vendor scoped Kagi commits(初回importを含む13件)
完全listing一次資料: https://github.com/TomiXRM/kagi/commits/91d2d10570cd88fc8e43151c8bb0a63003bb2a4a/vendor/gpui-terminal/
各SHAの https://github.com/TomiXRM/kagi/commit/ と .patch を確認。日付はpatch author dateの日本時間表記(GitHub commit一覧の日付timezoneと1日違う場合あり)。
出自/import
00c43470e4b816ab3d116e1de19e4805667a3d12: gpui-terminal0.1.0全体vendoring、MIT/Apache license・cargo provenanceをそのまま保持。view.rs末尾『macro issueでtests omitted』コメントは初回importに既に存在し、Kagiがview testsを削除した証拠ではない。挙動差:9件
0ccae1a9bb0b68e65e78f68120e349db4963d43e: 選択/Cmd copy。colors.rs selection、mouse.rs selection helpers、render.rs highlight/paint API、terminal.rs extraction/search tests、view.rs mouse state/paint geometry/key handling。vendor以外にapp terminalも変更。7774a843cd292af6faab6d551bd156b978046c09: render.rs display_offsetでhistory描画/cursor条件、view.rs scroll_display。shell login/interactive argsはKagi app側。cd36c0e865a15031e51f87dfced553957894e6d5: input.rs platform chordのPTY文字漏れ防止、tests、manifest dev dependency。967ee1f0c3f27db7d5be44ff89b8089f65bb8f71: 前guardの過剰抑制を修正。cmd-backspace→0x15、cmd-left→ESC[H、cmd-right→ESC[Fを例外化 + test。editor remediation全体commitのうちvendorはinput.rsのみ。abff9a3eb7c58151c3f943bd1f477590a1a01e88: event.rs queue、terminal.rs process_bytes戻値、mouse.rs SGR/legacy modifiers/button/motion/scroll dispatch、view.rs即時response書戻し/Shift bypass/alt-screen scroll等。+704/-52のうちdocs ticket128行を含むため全部実装量ではない。9f75e5603b35ea593b81cf3f8e7c5fd346ff5afc: vendor view.rs paste_text追加、DECSET/DECRST2004をemulator stateから参照、plain改行はunapproved時zero-write。確認modal、owner/spawn、raw Tabとclipboardの分離はKagi側。d5c005e5ddfc6c2301cc013f710d432c05d1ca7b: bracket payloadのESC/ETXをallocation-free retained slicesで除去。CR/LF/他UTF8を保持。app実PTY respawn coverage。5ff33c0bfad36d0c4a4e74ea9802c14fa65f516f: plain C0/DEL確認guard+shared predicate、localized warning/実PTY tests。dfccc4a508b5b2e189930050481dae33329ba561: predicateからclipboard Tabを除外、CR/LFは別warning、docs/testを整合。現在の仕様はC0/DEL『except Tab』。機械的移行/format:2件
a8a6be4664a6af25308e09a698dd73b485b0fdc4: vendor render/view formatting/clippy cleanup。他多数ファイルのcommit。287b46ebe8226c161a7cd5f8712f943e1ece4207: Cargo manifest GPUI git/分割platform、main.rs Application、render.rs paint signature、view.rs quad差への対応。独立のterminal機能追加ではない。test削除:1件
8853fcff4e1b5e7e4653ce779a2f0f479c6d412a: clipboard.rsのtest_clipboard_creation/copy_paste/clear 3件とevent.rsのtest_clipboard_store_event/load_event 2件を削除。後者2件はflume/proxy上のunit testsでOS clipboardを操作していないので、5件全部を『native clipboard isolationのため必然』と説明するのは不正確。production clipboard API/enum処理は残る。mouse/input/selection/query等Kagi追加testsは残っており『全test削除』でもない。7. 所有・license・公式crate方針の推薦
「GPLなのでMITと法的に非互換」と解釈するのは誤り。#1479の不採用判断は配布方針として読む。 current terminal/terminal_view Cargo.toml:6は
GPL-3.0-or-later(GPL-onlyではない)、各LICENSE-GPLはroot GPLv3本文参照。https://github.com/zed-industries/zed/blob/c403bc6af644cd0ab60c90045ac6dc3436218809/crates/terminal/Cargo.toml#L1-L6 と同commit crates/terminal_view/Cargo.toml、root LICENSE-GPL。MIT/ExpatはGPL compatible: https://www.gnu.org/licenses/license-list.html#Expat 。MITコードを含むGPL配布は可能だが、GPL-covered terminalをリンク/転写したcombined workを配布するならGPL条件・Corresponding Source・notice等が問題になる。非GPL/MITのままのKagi配布方針とは両立しない、という政策上の却下が正確。ADR-0031の『非GPL配布想定』は根拠になるがStatus Proposedであり、法律上の一般的禁止ではない。設計概念の参考とGPLコード転写も区別する。
選択肢:
A. 公式gpui-terminal0.1.0/mainをas-is: permission licenseは合うが上記要求を欠き、現在Kagi GPUI型とも違う。今の挙動を維持したまま単純に採用する案は推薦しない。
B. 公式alacritty_terminal + portable-ptyを無変更依存にして、GPUI adapterをKagiコードで保守: existing Kagi session/supervisor/resize/approvalをそのまま利用。Rust/UI部だけ
TerminalWidget { term, parser, writer, selection, font, geometry }としてKagiの責任境界を明確化。parser response→writer before notify、emulator mode authoritative、paste framing/filter/approval、selection/scroll/report、フォントとCJK幅を実装/移行対象に含め、現在の挙動を削らない。alacritty licenseはApache-2.0: https://docs.rs/alacritty_terminal/0.25.1/alacritty_terminal/ と https://github.com/alacritty/alacritty/blob/master/LICENSE-APACHE 。portable-pty existing APIはそのまま利用できる。これは『official crateを直す』のでなくapp-owned adapterでありowner方針に最も近い。C. existing vendorをKagi-owned crateへ移す: 保守責任は明瞭になるが 外部コードの派生物/実質forkである事実は残る。単なるname/path/license変更はfork解消でもupstream採用でもない。コードを再利用するなら作者copyright/原license/由来commitを保持しなければならない。original
.cargo_vcs_infoを現ローカルcrateの現在commit証明として誤表示しない。provenanceを専用notice/manifest metadataへ記録する設計はあり得る。D. 別emulator: wezterm-termはMIT、公開source https://github.com/wezterm/wezterm/blob/main/term/Cargo.toml と LICENSE.md。ただしGPUI adapterは別途必要でdrop-inではない。COSMIC terminalはGPL-3.0-only(GPL-3.0-or-laterではない)、https://github.com/pop-os/cosmic-term/blob/master/Cargo.toml#L1-L12 と LICENSE。MIT-only方針の代替にはならない。
vendor reuseの義務:
vendor/gpui-terminal/LICENSE-MIT:1-19: Copyright 2025 Leonard Seibold + permission noticeをcopies/substantial portionsへ含める。MIT OR Apache-2.0なのでMITを選択できるが、作者表示をKagiに置換してはならない。結論: cosmic forkは 端末+status+test metricsの明示font統一を終えてから official0.19.0へ戻せる設計見込み [INFERENCE]。WASMは別途同梱font問題として分離。terminalは公式gpui-terminalの単純置換で保持できる機能ではなく、official emulatorを使うKagi adapterか、由来/notice付き派生vendor保守という選択を明示する。『フォークではなくKagiのコード』というIssueの言い換えだけを解決として採用しない。
scoped commitごとの移行判定
00c43470(import): releaseのライセンス/由来を維持。機能patchではない。0ccae1a9(selection/copy)、7774a843(history scroll): official viewに同等完成経路なし。Kagi-owned adapterでalacrittyのselection/display_offset+GPUI hit/paintを使う。既存view外側だけのwrapperはprivate geometry/stateのため不十分。cd36c0e8、967ee1f0(platform chord guardと例外): Kagi keyboard boundaryでCmd入力をPTY文字にしない。Cmd-Backspace/Left/Rightは先に対応byte/actionへ解決し、残りCmdはconsume。公式ライブラリへのpatchは不要。abff9a3e(query/mouse): adapterのparser feedで生成responseをwriterへ渡してからnotify、emulator modeに従うSGR/legacy mouse・Shift bypass。opaqueなofficial TerminalViewをそのまま維持したい場合のみupstream対応が必要。9f75e560、d5c005e5、5ff33c0b、dfccc4a5(paste4段): current emulator modeをtruth sourceに、Kagi側confirm/preflightとadapter側framing/filterを維持。unapproved plain改行/C0/DELはzero-write、Tab例外、bracket payloadのESC/ETX除去を順序込みで移す。一般文字列を送るだけのofficial pasteに戻さない。a8a6be46(format): 挙動移植対象なし。287b46eb(GPUI供給元/paint API): #436でregistry preへ全caller同時移行。旧source互換shim不要。8853fcff(5 tests削除): upstreamへ要求すべき機能ではない。event proxy2 unit testsまで消えている事実を受入coverageに反映し、テスト削除をfork維持理由や移行成功の証拠にしない。全てをofficial TerminalView as-isで実現したいなら、private state/APIを開く上流変更が必要。しかし現在の責務をKagi-owned adapterへ置く案では、そのPRは撤去前提ではない。maintainedな外部emulatorを使うことと、UI adapterをアプリ側で持つことを区別する。
TomiXRM commentedon Oct 14, 2026
(続き)
§8 の検証結果
§8(a) 新しい選択機構と外側virtual-listのoffscreen投稿
4.1 公式の新機構が実際に保持するもの
U public
TextSelectionHandle(base/text_selection.rs:800-1017)にはfallback text、copy_with、subscribe、register、scope/document_order、content-key resolverがある。Copyは文書順へsortする。したがって『upstreamに横断選択の仕組みが全くない』は誤り。しかしregisterはgeometryを持つpaint participant。
TextSelectionRegistration::with_rendered_element(:244-270)はGPUI keyed element stateのweak markerを取る。cached replayでpaintしないframeもmarkerがliveなら保持する。実outer virtual unmountではmarkerがなくなり、finish_frame(:1282-1320)がparticipantをregistryから削除し、そのlocal selection/clear handlerを発火する。Copy
copy_items(:1523-1533)は残ったregistry entriesだけを列挙する。endpoint resolve(:1051-1056)はparticipant登録とweak ownerを要する。TextViewはprivateTextViewSelectionAdapter(base/text/state.rs:129)が自分のhandleを持ち、TextView.paintが登録する(text_view.rs:893-901)。retainしたTextViewStateだけではpaint participantのmarkerを延命しない。結論(コード上の事実):
Entity<TextViewState>を会話全投稿で保持する現在のKagi設計を、公式UへAPI名置換するだけではoffscreen selectionは保持できない。public copy_withの存在だけで、registryから消えた投稿がCopyへ入ることもない。ContentKeyはgeometry endpoint内のstable block identityであり、全unmounted投稿membershipの代替ではない。4.2 単一TextView内のoffscreen blocksは別に対応済み
U
TextViewSelectionAdapter::VirtualBlockSelection(selection_adapter.rs:16-77)はendpointにblock indexをcacheし、coverage Full/FromStart/ToEnd/Boundedでrangeを復元。ParsedDocument::selected_text(document.rs:91-154)はrange内部の未paint blockをwhole rendered textで埋める。大きい単一TextViewの内部virtual blocksは扱える。だから『#3421の制限→どんなoffscreen選択も不可』も誤り。正しくは『outer listが独立TextView participantをunmountする場合は不可、retained participant内のinner virtual blocksは対応』。
4.3 Fが追加した決定的な違い
F logical_selection.rsのGroupはstable post IDs+weak TextViewState+accepted rendered revisionの全membershipを持つ。Endpointはpost/revision/leaf/UTF8offset/affinity。paint geometryはhit authorityだけで、Copy sourceではない。
logical_selected_textはstart..endの全membersをwalkし、各parse-owned RenderedDocument::write_rangeを読む。中間投稿は全canonical rendered bytes、端の投稿は正確なpartial range。未paintでも文字列は完全。viewport leaseはlive必要で、unmounted inline geometryだけは失効する。ae37bfd4はfocused postを保持するdispatch fallbackも別途追加。公式はこのunmounted dispatch authorityを公開していない。4.4 fork無しの具体案と、その限界
案A:一つのretained TextViewへ会話をまとめる [INFERENCE]。
PreparedBodyをreading orderで一つのmanaged Markdown documentへ投影し、author/time/chromeはblock pluginで描き、single TextViewの内部virtualizationへ寄せれば4.2の公式range/copy engineを使える。Reply composerは同scroll surface末尾へ配置する必要がある。no outer post unmountなので問題の経路を消せる。
ただし現行契約のdrop-inではない。単一source更新は選択をinvalidateするため、未選択投稿のprepend/reorder/editでselected true Bだけを維持するKagi契約を、そのまま満たすとはいえない。Ctrl/Cmd-Aも単一文書全体になり、既存focused postだけ選ぶ意味が変わる。author chromeのCopy除外、画像/link/literal-codeのcanonical bytes、one-scroll composer、stable anchor/per-post accepted reuseも設計し直す。これを『そのまま撤去可能』としない。ユーザー承認なしの選択機能削減も提案しない。
案B:Kagi所有のdocument adapter/rich component [INFERENCE]。
official base TextSelectionHandleのpublic登録/subscription/copy callbacksを使い、Kagiがstable accepted document+logical interval+viewport lifetimeを所有。Kagi-owned rendererがvisible runsだけgeometryを供給し、Copyは全accepted rendered streamを読む。これならdependency forkは不要で現契約を維持できるが、公式TextViewのprivate selection adapterに外部handleを差し込むAPIはない。現在のstyled TextViewをただwrapするだけではexact shaped rendered endpointsもnon-mutating whole rendered textも取得できない。raw Markdownをconcatしてコピーする『代替』は契約違反。local rendererを所有するなら大きな保守負担として明示する必要がある。
案C:必要なextension seamをupstreamへ。
独自forkを避け、現在のper-post TextView/reconciliation/Copy契約を維持したいなら、次はTextViewに外部logical document adapterを接続する小さいpublic seamの提案が必要。提案sketch:post stable ID/revisionでcanonical rendered text/rangeを供給するdocument owner、visible TextViewがshaped caret offsetをそのownerへ報告するadapter、paint registrationとは別のlogical membership lifetime、explicit retirementとscope、outer viewport scroll callback。『全unmounted geometryを永続register』ではなく、論理authorityとhit authorityを分離する。#3421既存のpresentation retentionはそのまま再利用し、3.4k行fork丸ごとの移植とは分ける。
exact current behavior保持+official TextView as-is+outer per-post virtualization維持の3条件を同時に置くなら、Uの公開APIだけでは埋まらないseamがここにある。これが今回確認した本質的なupstream提案対象。PRがacceptされる確率はsourceから推定しない。
Kagi回帰契約:
tests/recovery/issue_conversation_selection.rs:27-57はforward/reverseのmousemove→Copyが次paint前でも全postsのcanonical bytesであること、:67-107は180段落giant body、:116-170はtrue comment ID reorder/prepend保持とselected same-byte-length mutationの同期retire。lifecycle suiteのsession departure/owner drop/modal/Input priorityも削らず維持する必要がある。§8(b)
measure_allの実測と、大規模リストの代替実測済み。数千行なら大丈夫という前提は採用できない。
ListState::measure_all()自体は測定モードを設定する builder であり、下表はそれを実行した 次の draw の CPU 時間。全行 element生成・CoreText shape/layout・prepaint・scene組立を含む。GPU present/FPSや Kagi 全体の応答時間ではない。条件:
gpui-pre=0.3.9/ platform=0.3.9。mainとlist.rsがbyte-identicalであることを確認してからこの公開版で測定。TestAppContext::build_with_text_system(TestDispatcher, native.text_system())+ native CoreText。MacPlatform headless、OS window無し、foreground activation無し。mock text shaper ではない。diff_view.rs:535–702に合わせた unified風の独立 row renderer: Menlo、text_sm、2×44px gutter、2 highlight span、EN/JAのRust行、7行に1行3倍長、40行に1 hunk、113行に1長いreview card。viewport1000×700→600×700、overdraw400px。実Kagi rendererの直接ベンチではない。N+14、warm14、resizeN+9、resize-warm9、resetN+9。可視行は全測定後さらに描画用に生成される。/tmp/kagi-fork-review/list-bench/{src/main.rs,src/bin/lifecycle.rs,Cargo.toml,Cargo.lock}、成功ログartifact://14972。独立 target は同directoryのtarget/。env CARGO_TARGET_DIR=/tmp/kagi-fork-review/list-bench/target cargo run --release --manifest-path /tmp/kagi-fork-review/list-bench/Cargo.toml --bin kagi-list-measure-audit、同じ引数で--bin lifecycle。xcrun metal不在で失敗→公式runtime_shadersfeature で解決。最初のdrawing harnessはEntity Render無しでcurrent_view panic→公式testと同じEntity Renderで修正し、上記2実験がexit0で完走した。ライブラリのpatchはしていない。lifecycle実験が示した、単純な代替案の穴:
scroll_to_end()は top=item999/300pxにして400pxのtailを下端100pxへ正しく合わせた。しかし他行が未測定なのでis_scrolled_to_end()は依然None。==Some(true)を末尾到達の唯一の根拠にできない。splice(10..11,1)。max40260→40220px、末尾判定None。次drawは可視行だけで、offscreen新規行の高さが0のまま。state.clone().measure_all()を再度呼ぶとmax40520pxへ回復。splice は measuring_behavior を再armしない(公式 :511–549)。性能ベンチでも同様にextentが69.5px減り、末尾判定Some(true)→None。remeasure()により比例anchorを用意した場合は50%が保持された。小規模の置換 sketch(
diff_list.rs:47–81/row_overlay.rs:86–104):1kでもこのモデルは60Hzの16.7msを超える。普遍的な「5k以下」等の閾値は提案しない。軽量固定高・短文なら異なるため、実row構成と許容latencyで閾値を決める。
大規模:
scroll_to_endは明示End/one-shot意図の実行には使えるが、正しいestimated extent、中間thumb→行index変換、splice、幅変更を解決しない。is_scrolled_to_endを組み合わせても足りない。したがって #1479の二方式だけで全契約を満たす案は 否。最小fork方向の設計 [INFERENCE・未実装]: Kagi側で単一の
DiffListState(stable RowKey、source/layout generation、Measured/Estimated高さ、prefix sum、anchor={RowKey,intra_row_fraction}、one-shot End、drag中extent)を持つ。GPUI ListState内部と二重所有しない。可視範囲+overdrawだけ既存row rendererを構築・計測し、実測差をprefixへ反映。source変更のみreset、spliceはRowKey anchorを保持し新規行をestimate、幅/font/gutterは世代invalidate。Endは最後から逆に測ってviewportを満たし、oversized tailも下端を合わせ、意図を消費する。公開APIで実現する範囲:
GPUI公開面:
src/element.rs:52–105,670–710,window.rs:4163,5209,5295,5432。公式 component のbase/src/scrollbar.rs:69–82が公開 handle trait。render_helpers、全diff owner、keyboard reveal、selection、thread expansionを同じstateへ切替える。非diff6 callersは本当に固定高のものだけ公式uniform_listへ、残りは可変高ownerへ。Zed list.rsを丸ごとコピーして隠れたforkを作る案ではない。現在はこの置換を実装・E2Eしたわけではないので、zero-patchへの設計上の道と受入ゲートを分ける。§8(c) 端末以外のno-fallback CJK経路
GPUI根拠:
経路一覧:
src/ui/mod.rs:3516、settingssettings_window.rs:35、E2E app roote2e.rs:894: ui_font()をRootへ設定。component Rootはview、tooltip、native_menu overlayを同じinner childとして描画(凍結gpui-kit crates/ui/src/root.rs:660-676)。通常label/SharedString、modal、tooltipはこのcascadeでカバー。conflict_editor.rs:878,1168,1215,1437,1565、render_helpers.rs:673(main diff)、kagi-ui-editor/src/lib.rs:3291(editor)、snapshot.rs:255、markdown.rs:480、kagi-ui-ecosystem/src/lists.rs:279、pr_conversation/hunk.rs:82、terminal/input.rs:115(paste warning)ほかmodal/metadata/Home/settingsのfamily変更。fallbackを消しておらずRoot配下では同梱Noto stackを維持。InputStateは作成時window styleでdisplay mapを作るが、Input elementのprepaintで現在style.font()を再適用してからprepare(gpui-kit input/state.rs:475-493、input/element.rs:1509-1518)。したがって実描画はRoot inheritanceを使う。TextViewもwindow.text_style由来fontでlayout/shaping(component text/text_view.rs:506-507,652-658)。kagi-ui-core/src/diff_list.rs:93のTextStyle::defaultはline_height_in_pixelsの推定にしか使わずshapingしない。commit_row.rs:345-372のwindow.text_styleはpaint probe内なのでRoot stackを見られる。これらをCJK no-fallback描画経路と数えない。検索範囲はKagi src/crates/tests/vendor、Font/FontFallbacks/TextStyle/TextRun、shape_line/shape_text/layout_line/line_wrapper、font_family/font setter、Root生成。source全体で追加の独自shape_line経路は上記status・font test・terminal以外には見つからなかった。
§8(d) 検索バーkeyboardと
cycle_focusコードで決着した。公式
component/src/input/search.rs:229–245のcycle_focusは replace modeかつreplaceableのとき2入力を交互focusするだけ。通常find-onlyではreturn、forward/backwardの順序も同じ。7 buttonsを含むTab順、Button Enterを裏Editorに漏らさないこと、padding/Buttonで編集actionsを止めること、disabled/disappeared targetからのfocus復元、names/help/JAは賄わない。no-fork代替は、built-in panelを
.searchable(false)で無効にしてKagiに2 named Inputs+7 named Buttonsを置き、matcher/replaceは公式set_search_query / next_search_match / previous_search_match / replace_current_search_match / replace_all_search_matches / close_search / search_sessionを再利用する。3 surfaces・6label設定をこのhostへ移す。full sketch・API行番号はKit節3.6。独自regex検索やraw replacementを増設する必要はない。§8(e) Radioのupstream受入可能性
styled Radioがcheck iconであることと、base Radioでdotを構成できることは確認した。upstream maintainerがdotをbugfixとして採用するかはソースから決められない。本調査ではPRやDiscussionを投稿していない。採用待ちをKagiのfork撤去条件にはせず、必要ならpublic base primitiveで既存見た目・AX・keyboard契約を維持する。
移行計画
公式依存・zero-patch の成立範囲
技術的に ZERO
[patch]は可能。ただし現行 Kagi の manifest から patch 行だけ消すことは不可能。 fork 固有の選択・検索 chrome・テスト seam・リスト使用法等を先に解消し、GPUI と component の型境界を一括移行する必要がある。独立した
/tmp/kagi-fork-review/resolve/Cargo.toml(Kagi tree 外、patch 無し)で次を実行した:cargo metadata --format-version 1: 成功、855 packages を解決。公式 upstream Kit1188c110…+ registrygpui-pre =0.3.9、全 tree-sitter languages と test-support、および web/wgpu の target 依存を含む。cargo tree --target all --edges normal,build -i gpui-pre: active core はgpui-pre 0.3.9一種類で base/component/assets/platform/macOS/Linux/Windows/Web が共有。cargo tree --target all --edges normal,build -i gpui-fast:nothing to print。metadata/lock に optional fast package が載ることと active な二重 GPUI は別。gpui-fastfeature は採用しない。/tmp/kagi-fork-review/resolve/{Cargo.toml,Cargo.lock,metadata.json}。これは dependency resolver の証拠であって Kagi build の証拠ではない。根拠: upstream
Cargo.toml:62–79、crates/component/Cargo.toml:17–21,120–131。上流 root の[patch.crates-io] rquickjsは shell workspace 用であり、Kagi が component/base/assets を依存として使うために継承する必要はない。standalone 解決で確認済み。gpui-baseは transitive に入るため crate split だけを理由に全129 production files の import を書き換える必要はない。component はRoot、focus traits、theme、Rope 等を再 export している(crates/component/src/lib.rs:98–123)。供給元の表現には注意:
gpui-pre 0.3.9は Zed 本家が直接 crates.io に発行した package ではない。registry API は publisherhuacnlee、descriptionsnapshot of zed@93d7456、公開 2026-10-12、Apache-2.0。platform manifest のzed-revは93d745668805029476e6d7fbb19652116fed51de。これは Kit upstream が指定する公開 snapshot を無改造で使う案であり、TomiXRM fork ではない。しかし「Zed 公式 publish のみ」という追加条件ならこの案を満たさない。その供給元判断を ADR-0001/0006 に明記する。根拠: registry API。また published
gpui-component 0.7.3と main は同一ではない。download package の.cargo_vcs_info.jsonは67265a36e260ffe03c5aaebdcd8d654dbf892eab(#3460)、今回の main は1188c110…。0.7.3 の依存もgpui-pre =0.3.9だが、main の新 API・修正が全部含まれると扱ってはならない。本調査の置換案は main 基準、release 化するときは対応版で再照合する。#436 の実測移行面
src/,crates/,vendor/,tests/の Rust source を対象。広域数値は literal 出現数(コメントと埋込 unit test を含む)であり、すべてを変更する数ではない。下記の builder 等は実際の grep hit を読み、実コードの呼出箇所を区別した。集計は/tmp/kagi-fork-review/migration-counts.json。gpui_component参照InputStateInput::new.multi_line(true).code_editor(...).auto_grow(...).wrap_row_count().search_labels(...)gpui_component_assetsgpui_kit_assetsfocused_input()Theme::global_mutwith_uniform_item_heightreset_with_uniform_heightmultiline/editor の field 名(body_input / pr_comment_input / analyze_ignore_input / ConflictEditorInputs / EditorInputs)だけでも20 production files・23 test files に広がる。これは型推論でそのまま通る consumer も含むレビュー集合で、確定の変更ファイル数ではない。
Entity<InputState>等を混在 array/chain にしている箇所は分離必須(commit_panel_composer.rs:56,compact.rs:128,issues_composer_e2e.rs:289,395)。timeline_row.rs:201は現状同じ Input helper で PR の textarea と Issue の editor を描いており、2つの型別 render entry へ切り分ける。single-line subject/title を multiline に変えるのは不可。Commit bodyの注意: 現実装は外側composerが縮められるよう、cached row数からpreferred heightを算出して内側Inputをh_fullにしている。Textarea auto_growだけでこの縮小契約まで同じとは未確認。S6では同じviewport受入を満たす公開layout観測/構成を採るか、Textarea measurement seamを上流へ提案する。Editorに型を変えてgetterだけ得る方法はkey behaviorも変えるのでdrop-inとして推奨しない。
型移行の具体例:
上流根拠:
base/src/input/base/state.rs:11665,11716,11734,11744、component/src/input/{textarea,editor}.rs、component/src/input/state.rsのAnyInputState、component/src/theme/mod.rs:459–478。Theme::global_mutの public fields を変えるだけでは Base scrollbar/text defaults は古いままになる。この同期漏れは単なる compile 対応では見つからない。Input key context は今もInput(base/src/input/base/state.rs:135)、Editor はInput mode=editor。Kagi の!Inputの Git-history shortcut guard を闇雲に改名する必要はない。テスト専用 fork API の代替は既に公式 API がある:
gpui-kit::test::{TestWindowExt, TestQueryExt}とElementSnapshot::{role,label,checked,focused,bounds}、gpui_base::TestSupportExt::test_support()。native AX props を読み、追加 layout node を作らない。kit/src/test.rs:28–74、base/src/test_support.rs:16–114、base/src/observe.rs:11–35。これは native VoiceOver の確認や Metal pixel 検査の代わりではない。Kagi の leak detector / actual keyboard dispatch / pixel assertions は残し、単なるテスト用の duplicate state 値で置き換えない。見積り: source inspection に基づく確定面は上表。少なくとも7 manifest + assets + typed-input/selection/find/theme/list 各 owner と対応 tests が対象。129 production files 全部を変更する必要はなく、逆に「7 builder だけ」で済む移行でもない。全 workspace を移行して compiler error 数を測ったわけではないため、総変更ファイル数や工数の断定はしない。
merge可能な slice と受入条件
「manifestだけ更新して次PRで型エラーを直す」は採用しない。GPUIの型は crate sourceを跨いで互換ではなく、その中間commitはdevを壊す。以下は 旧依存のまま準備を独立してmerge→最後の供給元/型切替は原子的 とする順序。各sliceで build・tests・対象native GUI E2Eがgreenでなければmergeしない。下記は後続実装の受入条件であり、本調査でKagiの検証を実施済みという意味ではない。
共通ゲート:
cargo build、cargo test --workspace(KAGI_LOG_DIR隔離)、関連純粋logic/integration tests、uv run --project ci check-all、format/clippy。GUIはKAGI_GUI_E2E=1 KAGI_GUI_E2E_ONLY='<対象substring>' cargo test -p kagi --features gui-e2e --test gui_e2e_runner -- --nocapture、複数scenarioは必要に応じKEEP_GOING。無filterの全GUI起動はしない。 OS/IME/VoiceOver/fontは対応実機のTier Bを別記録。bottom_panel。規約文がupstream PRだけでfork増加を許可しないterminal_,diff_,editor_。macOSだけで承認しないterminal_native、一切の削除で安全性を弱めないissue_,github_textの該当選択scenario。offscreen中間投稿、reverse drag、before-paint copy、per-post SelectAll、scroll中selection、presentation変更、tab/owner切替、deleted post、見える本文とcopy一致input_names,sidebar_search,settings_controlsと現行editor_find,editor_find_hostsがgreen。EN/JA、IME変換中と通常Escapeの分離、native AX。新APIを旧pinにあると仮定しないdiff_,pr_diff_extent,pr_lane,issue_,oplog_restore,inspector_の該当scenario。initial hint、offscreen splice、resize/zoom、same-count source、thread expansion、通常thumb中間/末尾、oversized tail、focus/reveal、one-shot not-follow。1k/5k/20kの実Kagi計時is_scrolled_to_end=Noneをfalseと誤解。公開API routeの実装量cargo treeでactive GPUI一種類、patch0。対象GUIをfeature別に分割して実施: commit-panel/undo, editor/find, settings/theme/radio, conversations, lists, terminal, platform-menu。real JA IMEとVoiceOver、Linux/Windows build、web build+既存Playwright specs、native pixel/leak testsS6を先にmanifest-onlyでmergeする案、caf50076を
[patch.crates-io] gpui-pre*へ載せ替える案、forkのtest-support hooksを消してE2Eを弱める案は採らない。上流へ小さい不具合PRを提案するのは有益だが、投稿すること自体を本調査では行わず、受入待ちを全sliceの必須依存にも置かない。継続のacceptanceは「patchが0だから完了」ではなく、既存の安全性と指定UXが保たれること。cross-post selectionを単一投稿copyへ落とす、Radioの丸をcheckへ戻す等はownerが別途仕様変更を承認した場合だけ。vendor terminalを維持するため、zero-patchはzero-derived-codeとは同義ではない。
方針 — CLAUDE.md / AGENTS.md に入れる不変条件の文案
以下は提案文であり、本調査ではファイルを編集していない。AGENTS.md を canonical とし、CLAUDE.md は同じ規則への参照を置くか同一内容に揃える。
既存 docs の具体的訂正対象: ADR-0222 の「推定高さ API がない」「diffだけ opt-in」、
docs/decisions.mdの同趣旨、Cargo.toml の fork 再追加を勧める旧コメント、ADR-0035 の「upstream が選択/copyを実装したら外す」だけの廃止条件、ADR-0001/0006 の source/pin 方針。cosmic-textpatch の削除条件は「upstream release を待つ」ではなく、明示font fallback と対象 Linux の受入確認でも満たせるように改訂する。未確認事項・実行証拠の境界
今回実際に行った検証
artifact://14972。TomiXRM commentedon Oct 14, 2026
オーナー判断(2026-10-15)
gpui-preを許容する → 目標は[patch]ゼロ。進め方
TomiXRM commentedon Oct 14, 2026
#1479 / #436 — fork 解消の確定実装計画(owner 決定反映)
0. 決定・基準点・この文書の意味
gpui-pre =0.3.9と同系列 platform/web/wgpu。Kagi の最終状態は 全 manifest の[patch]がゼロ。c0a4ea3ab6b3dc6aa68c2fdb2c379a4eefd53f64。本書の Kagifile:lineはすべてこのコミット。読取専用調査コピーは/tmp/kagi-fork-review/kagi-plan。元の作業ディレクトリ・worktree は変更していない。longbridge/gpui-component@1188c11003b5cb5b83bc07d3fb9112e7674fbfa7。main を無固定で追わず、component/kit-assets を同じ rev に固定する。gpui-pre は Zed 自身の crates.io リリースではなく、official Kit が採用する snapshot package である、という出自も明記する。local://fork-review-1479.md。fork 13 commits の全棚卸し、upstream 比較、依存解決証拠、ベンチ生ログは前報を継承。本追補は実装順序・テスト変更契約を確定するもの。[INFERENCE]の LOC 見積もりや proposed 閾値を実測値と混同しない。証拠リンクの読み方
Kagi:
https://github.com/TomiXRM/kagi/blob/c0a4ea3ab6b3dc6aa68c2fdb2c379a4eefd53f64/<path>#L<line>。Kit:
https://github.com/longbridge/gpui-component/blob/1188c11003b5cb5b83bc07d3fb9112e7674fbfa7/<path>#L<line>。ローカルの全
file:lineは上記 SHA に対応する。調査後 dev が進んだら、実装開始時に line mapping と衝突箇所のみ再確認し、既存の新しい変更を上書きしない。全 slice 共通の merge 規則
.claude/skills/verify/SKILL.md:49,515–532,590–592に従う。KAGI_GUI_E2E_ONLYは カンマ区切り部分一致、KEEP_GOING は明示 filter 必須。新 scenario は下記で「新規」と記した名前を登録する。存在しない filter で 0 件 PASS にしない。件数/LOC の定義: files_count は下記で列挙する予定変更パス(新規/削除含む)の distinct 数。受入のみで変更しないファイルは数えない。slice 間の重複はあるので合計は unique file 数ではない。LOC は追加+削除の churn(Rust/test/docs/TOML を含む、生成 Cargo.lock の行数は除外)の
[INFERENCE]幅。未ビルドのため最終 S6 の compiler-driven minor fixes は予備幅で明示する。1. 追加調査とownerによる仕様解釈の確定
U0は不要 — mounted cross-viewを禁止するscopeは作らない
公式のTextViewはwindow-level selection controllerを共有し、mounted複数viewのdrag/Shift-clickを自然に扱う(official
component/src/text/window_selection.rs:1703,1858)。独立scopeを作るとComponent Rootのdefault/modal scope管理と競合するが、ownerはその独立scope/境界保証を求めていない。したがって per-view selection-boundary APIのupstream PRは作らない、待たない。Kagiのgroup/lease/全post canonical interval/outer edge autoscrollを除去し、公式TextView/default scopeをそのまま採用する。公式に残るmounted cross-viewを失敗とする負条件テストも追加しない。「単一投稿の正のCopy証明」と「Kagi独自の画面外全会話保証を終了すること」を分ける。
旧報告の独自rich-selection renderer案、追加調査途中のstrict per-post scope案はいずれも撤回する。依存graph上のzero-patchは確認済みであり、selectionに関する外部prerequisiteはない。
composerの公開API構成は最後に別途確定
現
commit_panel_composer_render.rs:28–35,79–91のpreferred3–12行と低windowでのshrink/internal-scrollは引き続き維持する。Textareaのauto_growはleaf min-heightも増やす (base/input/base/element.rs:2518–2524) ので、単にauto_grow+外側min_h0では同じでない。Kagi側の高さ設定+plain Textareaの内部scrollという公開API構成を先に検討し、必要がある場合だけ公式cached-row getterをprerequisiteにする。最終判断と具体的構成はS6に記す。不要なEditorState偽装・別shaper・forkを先に選ばない。S0 — 方針と出自を固定する(最初に merge)
依存: なし。merge unit: 文書・現行コメントのみ。依存更新・API変更なし。
変更パス(6):
AGENTS.md:13–37: upstream-as-is / zero-patch / owner 承認された範囲変更 / Kagi-owned boundary の不変条件。docs/adr/0001-gpui.md:21–30: 0.2.2 固定・platform 未公開という歴史記述を保持し、新決定 gpui-pre を追記。docs/adr/0006-gpui-component.md:19–30,43–44: 0.5.x 留保を supersede、official main 同一 rev / typed API 移行を採用。Cargo.toml:177–179:「cosmetic polish が欲しければ fork を re-add」の指示を削除。今ある patch をここで早まって削除しない。docs/decisions.md:25,143–146,279,300–302: 過去決定を書き換えて証拠を消さず、先頭に新しい superseding decision を一件追加。CHANGELOG.md:6: migration 方針の変更を記録。CLAUDE.mdは git mode 120000、内容AGENTS.mdの symlink。別内容を複製・上書きしない。物理変更ファイル数は AGENTS 一つで数える。不変条件の文案:
regression/gate: docs のリンク・CLAUDE symlink維持、Cargo の実依存内容が不変。共通 gate の既存 baselineを利用し、新しい native scenario は不要。LOC: 90–160。
S1 — 明示 fallback を全 native shaping 経路に通して cosmic patch を除去
依存: S0、S2(terminal 所有方針を先に確定)。merge unit: fallback plumbing+テスト+cosmic patch/lock除去を一緒に merge。component / Zed pin はまだ現状維持。
production の変更位置
vendor/gpui-terminal/src/view.rs:141,170,505,1208–1220TerminalConfigにfont_fallbacks: Option<gpui::FontFallbacks>(既定 None)、renderer作成・live config変更へ渡す。Kagi caller が明示するので汎用既定を勝手に Noto にしない。vendor/gpui-terminal/src/render.rs:170,213,247,633src/ui/terminal.rs:111–132build_terminal_configがkagi_ui_core::fonts::ui_font().fallbacksを入れる。539–551,563–620の初期font選択と既存session更新経路をそのまま通す。src/ui/commands.rs:2082–2114src/ui/render_status.rs:87–103(caller181,191,335)Fontに Root の fallback が継承されるという誤解を除去しui_font()由来の shaping と描画を一致させる。vendor/gpui-terminal/src/view.rs:113/src/lib.rs:60,147/src/main.rs:130/README.md:48render.rs:54,211,725,768も同時変更。Cargo.toml:171–176/Cargo.lock[patch.crates-io]も残さない。docs/adr/0130-bundled-japanese-font-fallback.md:29–31regression を先に置く
crates/kagi-ui-core/src/fonts.rs:28,40,58,87–137の既存実 CosmicTextSystem テストを拡張。ABC日本語直骨誤、Normal/Bold、UI Inter / terminal JetBrains Mono / 実在する Nerd 系 primary、ASCII と2-cell CJKを対象にする。run が2個あるだけでは不十分。同じ text system で primary を明示Noto Sans JPにした参照 shape の日本語 run FontId と一致、ASCII run は primary、Regular/Bold は対応する別 static face と照合する。非公開TextSystem::font_idを呼ぶ設計にはしない。vendor/gpui-terminal/src/render.rs:724–779付近の renderer unit とview.rsconfig update unit: clone/new/live update の fallback伝搬、font変更でmetrics再計算、null/defaultの汎用契約を確認。tests/recovery/terminal_fonts.rs:terminal_font_fallback登録。実terminalのfont切替・zoom・セッション切替・再起動で同じconfigが伝わること、ANSI bold / CJK2セル / ASCIIカーソル位置を確認。PTY に日本語を送れたことだけを glyph保証としない。tests/recovery/status_bar_fit.rs:202,time_locale.rs:112,density_rest.rs:97–120,finder_scale_editor.rs:88–95,finder_scale_history.rs:78–85,finder_scale_rest.rs:137–141,home_heading.rs:65–73,inspector_scale.rs:91–98。Window.text_style 外で Fontを作るprobeは Rootを継承しない。Linux の必須受入(macOS PASS で代替不可)
cargo test -p kagi-ui-core linux_cosmic_text_uses_bundled_fallback_for_regular_and_boldと新しい同系font単体テスト。既存new_without_system_fontsを使い、bundled facesのみで ASCII/日本語が正しい face にshapeされることを検証。ja_JP.UTF-8とen_US.UTF-8の新規プロセス、system Noto SC/JP がある環境と最小font環境の両方。個人PCのfont削除/グローバルfontconfig変更は禁止。用意したVM/containerのprivate fontconfig/cacheで行う。ABC 日本語 直骨誤を通常/ANSI boldで出し、同じwindowに明示 Noto Sans JP の参照描画を並べたテストfixtureまたは隔離 captureで比較する。Regular/Bold・glyph形・2-cell位置・selected font と fallback の役割を残す。native画像はpid指定captureを使う。Inter根の一般ラベル、status fitting、commit/Issue Input、TextViewでも日本語が同じfallbackになることを確認。tests/gui_e2e_runner.rsは macOS gated moduleが多い(例48,872–891)。このrunnerをLinuxで空実行してPASSとせず、Linux実shape unit+native実機/Tier Bを独立証拠にする。S6後の pre移行でも同じLinux gateを再実行。native filters(既存+新規):
terminal_,status_bar_,relative_time_ja,cleanup_local_date,oplog_row_typography,issues_row_secondary_text,finder_scale_rest,home_heading,inspector_scale。finder_scale_restが editor/history のinspectを内部実行する(同file:113–114)。terminal_font_fallbackは新規登録を必須にする。terminal paste / control / focus / auto-lock の既存oraclesは変更しない。変更パス24: 上表のroot2、vendor5、production2、fonts1、計測test8、新nativetest1、runner1、ADR-0130/decisions/CHANGELOG/verify skill4。LOC: 550–950(lock再解決を除く)。リスク: font fallback fieldを初期化時だけ渡してlive設定を落とすこと、実測probeと描画fontの乖離、Linuxで偽の空suite。
S2 — terminal を Kagi-owned と明文化(S1 より先に merge 可)
依存: S0。merge unit: 所有/出自/noticeの説明のみ。terminal実装・公開挙動は変えない。
変更パス(6):
docs/adr/0035-vendor-gpui-terminal.md:1,14–30:「temporary fork」「upstreamにselectionが来たら即削除」を撤回。terminal frontend/selection/PTy統合はKagi所有、VTEは引き続きofficialalacritty_terminal、upstream出自は追跡する。vendor/gpui-terminal/README.md:1–3,60–67: upstream一般説明とKagi変更を区別し、既に実装済みのselection/scrollbackを「未実装」とする古いlimitationを除く。vendor/gpui-terminal/Cargo.toml:1–10,23–26: normalized registry copyのまま「自動生成で編集しない」という誤説明を修正し、Kagi-owned packageの説明とupstream originを両方明示。名前の変更・directory移動はしない。元authorの出自は消さない。Cargo.toml:51: vendored forkという一時性コメントをKagi-owned/ADR参照に更新。docs/decisions.md,CHANGELOG.md。license:
LICENSE-MIT/LICENSE-APACHEと attribution をそのまま保持。MIT OR Apache-2.0のupstream由来をKagi著作だけに置き換えない。Zed terminal(GPL)への転写・丸ごと置換は採らない。機械的 rename とemulator rewriteは本計画のscope外。regression/gate: code diffがないこと、packagingにnoticeが残ること。既存
terminal_paste_bracketed,terminal_paste_control,terminal_focus_exit,terminal_hide_focus,terminal_states,terminal_auto_lockを隣接baselineとする。LOC: 110–190。リスク:「owned」を理由にupstream noticesを消すこと、単なるdirectory renameをfork解消と誤認すること。S3 — 投稿単位の正条件を固める準備(旧 S3 を置換)
依存: S0。merge unit: 現forkでも満たせる単一投稿の正条件、mounted/unmounted witnessのみ。実際のgroup除去・公式挙動へのoracle切替・ADR現仕様改訂は S6の原子的cutoverに統合する。これにより旧pinの非managed default selectionと新official controllerを二度実装・検証する寄り道を避ける。
変更パス4:
tests/recovery/issue_conversation_selection.rs,tests/recovery/issue_offscreen_select_all.rs,tests/recovery/issue_conversation_support.rs,CHANGELOG.md。selection.rs:67–114: 巨大単一postのexact Unicode/rendered-text Copyを明確にする。selection.rs:116–195: reorder/prepend後に選択postが本当にmountedであるwitnessを追加。outer-unmountでもretainする旧要求と混同しない。offscreen_select_all.rs:76–162: unmount前のmounted partial→Cmd-A/fullの正条件を追加。旧offscreen oracleの改訂はS6まで待つ。:168–177,223–238: 新frameのpaint bounds witnessを使う既存physical drag/poison clipboard helperを再利用。graph_focusもimportするのでmoduleごと消さない。gate filters:
issue_conversation_giant_copy,issue_conversation_accepted_identity,issue_conversation_offscreen_select_all,graph_copy_foreground。LOC: 250–400。準備テストだけで仕様変更完了と呼ばない。S6で切り替える既存13 scenarioの全oracle
登録
tests/gui_e2e_runner.rs:4089–4138。公式の偶然のmounted cross-viewを禁止するassertは設けない。新方針で不要な旧保証を、明示的に意味を変更した単一投稿testへ置換する。issue_conversation_offscreen_copy/4089 (selection.rs:27–65)issue_conversation_giant_copy/4093 (:67–114).scrollable(false)なので内部block virtualizationの証明ではない。issue_conversation_accepted_identity/4097 (:116–195)issue_conversation_refresh_anchor/4101 (:197–255)issue_conversation_geometry/4105 (:256–286)issue_conversation_edge_drag/4109 (:288–372)issue_conversation_delayed_rejected/4113 (lifecycle.rs:124)issue_conversation_return_pending/4117 (lifecycle.rs:50)issue_conversation_return_failed/4121 (lifecycle.rs:89)issue_conversation_two_owners/4125 (lifecycle.rs:199)issue_conversation_copy_priority/4129 (lifecycle.rs:314)issue_conversation_enabled_input_copy/4133 (issue_enabled_input_copy.rs:75–164)issue_conversation_offscreen_select_all/4137 (issue_offscreen_select_all.rs:76–162)画面外単一投稿の全文取得は選択controllerで復元しない。後述の調査のとおり、既存の投稿本文Copyボタンも存在しない。目的の投稿を再表示して公式操作する方針であり、会話全体Copyの代替機能は新設しない。
S6で追加する実native scenario(新規登録)
tests/recovery/conversation_official_selection.rs(新規):issue_conversation_post_select_all: mounted body/comment各単独のpartial/full、rendered Unicode/GFM/コードとprivacy、公式Copy/SelectAll。pr_conversation_post_select_all: description/review/commentの単独Copy、同number別repo/session、accepted source差替え。既存pr_same_number/pr_threadsはclipboardを検証していないので代用不可。pr_thread_post_select_all: inline thread comment単独のpositive。既存full-hunk Copy Buttonは別契約のまま。issue_conversation_official_rerender: mounted partialを保った実Root再render・chrome更新・theme/width/zoom。default scope維持を確認。issue_conversation_giant_internal_selection: bounded parent + official.scrollable(true)のテスト用実wrapperで同TextViewがmountedのままinternal blockを仮想化し、single-post全文Copy/内部edge task停止。productionを勝手にnested-scroll化しない。unmounted focus: 公式のactual-unmount cleanup (
base/text_selection.rs:1294–1323,base/text/selection_adapter.rs:147–158) を採用。retained Entityを理由に旧postへSelectAllを転送しない。Kagi既存のfocus回復経路を使い、隣post/Replyへ自動SelectAllしない。Input/modalが既にfocusを持つ場合は触らない。独自scope/presence registry/全post走査を新設しない。source replacement/departure: official set_textはprefix追加をstream appendとしてselectionを保持する (
base/text/state.rs:315–335)。accepted snapshot差替えはstreamではないので、selected旧sourceを明示clearしてから入れ替える。source/deletionはcxを持つaccepted mutationで同期clearする。session/modal/Home離脱は既存Kagiのactive/generation guardをCopy/SelectAllのcaptureで検証する(詳細S6)。任意のno-App DropがSDK内部stateまで即時clearするという旧lease固有保証は維持しない。S4 — 旧 pin 上で Clear / IME / Input AX oracle の依存を外す
依存: S0。S3と共有するrootファイルはrebaseで統合。merge unit: 旧pinで動くpublic Input suffix・GPUI action境界・実native AX inspectionのみ。Find engine / Radio / typed statesはここに混ぜない。
対象と実装契約
src/ui/sidebar_panes.rs:500–519の唯一のclear_button_aria_labelを削除。Input::new(...).aria_label(...).cleanable(false)とpublicsuffixに既存Kagiのnamed Buttonを置く。空ならsuffix無し。押下は同じInputのclean(window,cx)と focus復帰。表示size/hitbox/tab順・locale・query ownershipはそのまま。別TextInputや名前付けだけのGroupを増やさない。src/ui/sidebar_focus.rs:195–218の「非empty Escapeはclear+Input維持、empty Escapeはlistへ」を受入契約として維持(原則変更不要)。IME中のEscapeがそれを先に実行しないようにする。crates/kagi-ui-core/src/input_actions.rs、exportをsrc/lib.rs:21付近に置く。WindowExt::focused_inputとEntityInputHandler::marked_text_rangeによる action開始時 のIME状態観測を行う。src/ui/render.rs:690–703, statesrc/ui/mod.rs) と独立Settings root (src/ui/settings_window.rs:316–372) の既存containerにinput::Escapeのcapture/bubble境界を設置。二段式: captureで現在のfocused Inputのmarked有無をwindow-ownerのboolに上書きしてcx.propagate()、kit childが通常のmenu/multicursor/inline completion/touch selection/IME順で処理、bubbleまで来て開始時markedならそこでconsume、そうでなければpropagate。次のcaptureで必ずresetする。renderごとのRc/Vec allocationやglobal別selection registryを作らない。base/src/input/base/state.rs:2176–2213のmenu→cursor→completion→touch→IMEという優先順位を飛ばさず、IME unmark後に別のEscape binding(sidebar clear / modal close)へ落ちることを止める。単純なroot早期unmarkはその優先順位を壊すので採らない。.clean_on_escape()callerがないことをgrep確認済み。将来同設定を導入する場合、childがclearする前に止める必要が生じるのでこの契約も同時改訂する。本sliceは勝手にclean_on_escapeを有効化しない。AnyInputState::{Input,Textarea,Editor}matchへ変えるだけ。型消去のために全入力を旧InputStateへ戻すshimは作らない。tests first
tests/recovery/sidebar_search.rs:119–146/sidebar_filter.rs:33,124/input_names.rs:10,30–38,66,100,112のtake_rendered_inputs_for_test/rendered_aria_labelを 実AX tree oracleへ移す。tests/recovery/native_ax.rs:21–38の既存Window::debug_a11y_tree_jsonを再利用。tree["gpui_focus"]が指す実nodes[id]["aria"]["role"/"label"]を見る既存先例はblame_toggle.rs:23–26,51–55。外側Group名や宣言定数をassertして終わらせない。input_ime_escape_boundariesを既存tests/recovery/input_names.rs内に追加しrunner登録。Sidebar・Commit body・Issue editor・独立Settings textareaで、IME中のEscapeが祖先のclose/clearに届かず、通常Escapeの既存順序が生きることを検証。menu/completionが開いている場合はその優先消費を保持。button focus時はhidden editorにediting actionを送らない。editor_findの forkSearchPanelProbeは まだ削除しない。新しいsearch engineがない旧pinで代用品を先行投入すると二重実装になる。S6で実hostとoracleを同時置換する。変更パス15: core新helper/lib、root mod/render/settings_window/sidebar_panes、input_names/sidebar_filter/sidebar_search/native_ax、runner、ADR-0014/decisions/CHANGELOG/verify skill。
gate filters:
sidebar_search_,sidebar_filter_tab_entry,input_names_inventory,input_ime_escape_boundaries,settings_,commit_panel_body_focus,compact_rail_cmd_f(新規名を登録したことをrunner summaryで確認)。LOC: 450–750。リスク: captureだけでkitのpopup優先を潰すこと、前eventのmarked flag再利用、native TextInputではなく名前付きwrapperを数えること。S5 — zed list fork を不要にする(4つのgreen merge unit)
現snapshotの実検索:
with_uniform_item_height7 callsites / 7 files、reset_with_uniform_height1、measure_all0。7 listsという意味ではない。shared diffは6 retained owners、restore helperは2 panesを支える。geometry所有を確定
core/diff_list.rs:71,78→ Main/History/PR/Conflict/Editor WIP/Editor Historysrc/ui/pr_lane.rs:237uniform_list。elision/commitとも.h(scaled ROW_H)(373,465)。src/ui/inspector_files.rs:140uniform_list。dir/More/fold/fileは同slot (284,299,335,396)。Unavailable (307–312)にも明示同height slotを与える。src/ui/operations/oplog_restore_inspection.rs:82uniform_list。refsoplog_restore_card.rs:122–123,393–420、previewoplog_restore_preview.rs:270–370。src/ui/issue_conversation.rs:104–105src/ui/issues_mode/feed.rs:186:46はmin_hにすぎずtitleは:146–153でwrapするのでuniform不可。src/ui/tab_view.rs:596–601pending threadissues_composer.rs:412–424)。小list用の第二geometry strategyを作らない。measure_all の採用閾値と実測を混同しない
本実装のproduction採用閾値は0(使用しない)と確定。理由は「全件計測で解決できないからゼロ」ではなく、実測で1k coldがすでに1frameを越え、唯一の小候補pendingも巨大previewを許すため、既に必要な共有variable ownerを使う方が単純だから。count-only threshold / 大list scroll_to_endだけ、という案は採らない。
実験上の安全上限を論じるなら ≤256 rows かつ total64KiB / row8KiB / total1024 projected wraps / row64 wraps、画像・table等cost不明なら不採用が保守的な評価候補。しかし256の測定は未実施であり、この条件分岐・feature flag・未実装budget modeを製品に入れない。将来使う判断をするならnative actual-row cold/resize/reset/splice p95≤8ms、warm≤1ms、全frame≤16.7msを先に測る。今回のmergeはこの将来評価を前提にしない。
既に実行したofficial gpui-pre0.3.9のCPU draw測定(再実行なし、
artifact://14972):M1 Pro/CoreText/release、EN/JA diff風rowとhunk/review card、各3fresh context。GPU present/実Kagi E2E/256rowsの測定ではない。
measure_all後のoffscreen spliceは再arm必要、automatic resizeだけではfraction anchor非保持、scroll_to_end成功でも未知heightがあればis_scrolled_to_end=Noneというlifecycle実測も前報どおり。共有variable ownerの必須契約(fork source copyではない)
crates/kagi-ui-core/src/variable_list.rs(新規)を既存pane/tabの寿命で保持。既存DiffListLayoutはsource/split/gutter identity、RowScroll/RowFocusはnavigation、既存genericwith_vertical_scrollbar(render_helpers.rs:791–806) はpublicScrollbarHandleを再利用。別のfocus/selection managerは作らない。shared diffの全callers(取り残し禁止)
core/diff_list.rs:47–81、constructorsrc/ui/render_helpers.rs:540–541、sync606–612、list674–721、keyboard mapping725–750。src/ui/main_diff_pane.rs:42–43,57–58,69–94,204–214。src/ui/file_history.rs:34–35,62–77,174–175。src/ui/pr_mode.rs:71–72,332–333,1765–1781、PR conflict101–102,345–346,578–588,1741–1745。crates/kagi-ui-editor/src/lib.rs:539–540,671–672/src/ui/editor_workspace.rs:62–70。crates/kagi-ui-editor/src/lib.rs:466–467,648–649,3394–3395/src/ui/editor_workspace.rs:86–94。src/ui/github_pr_detail_tests.rs:35–36,46–47。src/ui/diff_selection/keyboard.rs:79,165–200、overlay splicerender_helpers/row_overlay.rs:86–104、thread source invalidationpr_mode/threads.rs:49–70,330–337。crates/kagi-ui-core/src/keyboard_rows.rs:43–109にOwned variantを追加する。無関係なnative List variantは残す。S5-A — 既存keyboard adapterとPR lane(8 files / 250–450 LOC)
ListKeyboard::rows/focus_first(src/ui/list_keyboard.rs:19–55) をRowScroll引数へ一度で切替。全callerbranch_picker.rs:49,62,home_recent.rs:48,pr_lane.rs:238,oplog_restore_inspection.rs:83,issues_mode/feed.rs:75を同mergeで変更し、旧overloadは残さない。PR lane owner/newpr_lane.rs:38,46、reset230–231、render289をuniformへ。現在描かれたrangeは既存RowList::as_drawn方式で報告する。tests first:
tests/recovery/pr_lane_keys.rs:41,130,210にtail/Home/End/Page・window-key変更・zoom・実thumb extentを追加。branch/Homeはargument adapterだけでリスト方式は変えない。S5-B — Inspector / restore uniform(10 files / 450–800 LOC)
依存A。Inspector owner/new/reset/splice
inspector_files.rs:62–95、More末尾navigation142–145、render/scrollbar159,182–187を変更。既存generated-fold/file pathによるsurvivor focusを保持。restore owner/geometry/wheel/parent revealoplog_restore_inspection.rs:60–143,157–158、native専用E2E返値193–200をuniformへ。tests first:
inspector_history_keyboard.rs:63,153,384,388,inspector_file_rows.rs:152,oplog_restore_keys.rs:16,232,304–310,553,577。scroll可能paneは端へ達するeventを自分がconsumeし、次のoutward wheelだけ親へ渡す。no-overflow paneは親へchain。parent reveal後のpointer scrollを引き戻さない。Unavailable/More含む全slotに同height。ADR0214342–355のListState固有説明を更新。S5-C — variable owner + 6 diff owner全切替(22 files / 2,250–3,700 LOC)
依存S0(A/Bとは独立、core keyboard_rows共有編集は順に統合)。new moduleだけを未使用scaffoldとしてmergeせず、全6consumerと同時導入。
tests first:
core/diff_list.rs:106,132の同count別source/repaint/gutter/expansionの回帰を移植。row_overlay.rs:158,180,189のbase-row対応/countテストは残す。pr_diff_extent_unified(runner3128),split3132,reflow3136,threads3140,source_recolor3144,same_selection3148,editor_diff_extent_unchanged_reload3152。pr_diff_extent_same_selection.rs:77,189–260,editor_diff_extent_reload.rs:243–272をlive owned measured-height+実paint boundsへ変更。estimateを測定済heightと偽って返すListState互換testshimは禁止。S5-D — Issue variable geometry統一(12 files / 800–1,400 LOC)
依存A,C。accepted conversation
issue_conversation.rs:76,104–105,166,217,273–350、thread renderissues_thread.rs:87,127,165–207、feed ownertab_view.rs:439,582/ pending owner463,596–601、feed splice/pagingissues_mode/feed.rs:177–234を同ownerへ。現pinでgreenを保つため重要: S3は準備テストだけなので、この時点では既存forkのgroup/viewport callbackを、新geometry handleへ必要最小限に接続して現仕様を保持する。これを新APIとして増設せず、S6で既存groupごと削除する。S5-D単独でcross-postの負条件を有効化しない。最終selector/rendererを二重実装することとは違い、現行consumerのgeometry交換である。
tests first:
issues_pagination.rs:60,109–111,133,150,176,263,281、conversation selection/lifecycle、github_text.rs:135,181。same-count offscreen post/metadata/locale変更、reading PostKey前のinsert/delete、巨大composer preview、最終post/Reply到達、width/zoom、append時no-follow、filter中の無用paginationなし、pending→acceptedのfocus/scroll ownerを追加。opaque reset callers
github_issue_state.rs:52,90,307,github_issues.rs:206,236,issues_composer_e2e.rs:109,563は元のexplicit reset意味を維持できれば文字変更不要。reset-to-startとappendを混同しない。per-post semantic cutoverはS6で行う。S5 各merge unitの全変更ファイル・filters
S5-A — 固定PR laneと既存RowScroll adapter
依存: S0。files_count 8。
src/ui/list_keyboard.rssrc/ui/branch_picker.rssrc/ui/home_recent.rssrc/ui/issues_mode/feed.rssrc/ui/operations/oplog_restore_inspection.rssrc/ui/pr_lane.rstests/recovery/pr_lane_keys.rsCHANGELOG.mdgate filters:
pr_lane_keyboard,pr_lane_selected,pr_lane_count,home_recent_rows_keyboard,branch_picker_screen_departure。S5-B — Inspector・restoreのuniform化
依存: S5-A。files_count 10。
crates/kagi-ui-core/src/keyboard_rows.rssrc/ui/inspector_files.rssrc/ui/operations/oplog_restore_inspection.rssrc/ui/operations/oplog_restore_card.rssrc/ui/operations/oplog_restore_preview.rstests/recovery/inspector_history_keyboard.rstests/recovery/inspector_file_rows.rstests/recovery/oplog_restore_keys.rsdocs/adr/0214-oplog-panel-and-point-restore.mdCHANGELOG.mdgate filters:
inspector_files_keyboard,file_history_focus_return,inspector_generated_focus_retained,inspector_overflow_keyboard,inspector_file_row_hit,inspector_scale,oplog_restore_。S5-C — 共有variable geometryと全diff owner切替
依存: S0。files_count 22。
crates/kagi-ui-core/src/variable_list.rs(新規、実consumerと同時)crates/kagi-ui-core/src/lib.rscrates/kagi-ui-core/src/diff_list.rscrates/kagi-ui-core/src/keyboard_rows.rscrates/kagi-ui-editor/src/lib.rssrc/ui/render_helpers.rssrc/ui/render_helpers/row_overlay.rssrc/ui/main_diff_pane.rssrc/ui/file_history.rssrc/ui/editor_workspace.rssrc/ui/pr_mode.rssrc/ui/pr_mode/threads.rssrc/ui/diff_selection/keyboard.rssrc/ui/github_pr_detail_tests.rstests/recovery/pr_diff_extent.rstests/recovery/pr_diff_extent_invalidation.rstests/recovery/pr_diff_extent_same_selection.rstests/recovery/editor_diff_extent_reload.rstests/recovery/pr_detail_adaptive.rsdocs/adr/0222-diff-list-estimated-height.mddocs/adr/0209-pr-review-threads-on-diff.mdCHANGELOG.mdgate filters:
pr_diff_extent_,editor_diff_extent_unchanged_reload,diff_keyboard_selection,split_diff_keyboard_order,diff_selection_revision,diff_hunk_buttons,pr_threads,review_hunk_full,pr_detail_adaptive,pr_detail_lifecycle。S5-D — Issue feed・accepted/pending conversation geometry
依存: S5-A, S5-C。files_count 12。
crates/kagi-ui-core/src/variable_list.rscrates/kagi-ui-core/src/keyboard_rows.rssrc/ui/tab_view.rssrc/ui/issue_conversation.rssrc/ui/issues_thread.rssrc/ui/issues_mode/feed.rstests/recovery/issues_pagination.rstests/recovery/issue_conversation_selection.rstests/recovery/issue_conversation_lifecycle.rstests/recovery/github_text.rsdocs/adr/0198-read-only-issues-workspace.mdCHANGELOG.mdgate filters:
issues_pagination,issues_row_secondary_text,issue_conversation_,issue_body_zoom,review_hunk_full。Zed patch除去はS5単独unitにしない。最後のS6でroot
Cargo.toml:181–205(22 entriesは184–205)とlockをofficial package familyへ同時変更する。古いline番号181–202という前snapshot由来の値は使わない。S5A–Dのchurn計は[INFERENCE]3,750–6,350 LOC、同一ファイル重複あり。投稿 Copy ボタンの調査結果 — 「既存ボタンで代替」はできない
現c0a4ea3には、Issue/PR投稿本文全体のCopyボタンがない。 UI全体のCopy関連検索に加え、実際の全投稿rendererを確認した。
src/ui/issues_thread.rs:214–276、processor93–112src/ui/pr_conversation.rs:25–99、本文82–89src/ui/pr_conversation.rs:228–334,509–529src/ui/pr_conversation/hunk.rs:27–40,94–126,128–180、callerpr_conversation.rs:309ReviewComment.diff_hunk全量専用。投稿本文ではない。hunk buttonのIDは
("pr-convo-hunk-copy", number*1000+i)、clickは表示時のhunk.full.clone()をclosureに捕捉してCopyする。stable GitHub comment IDから本文を再検索するcommandではない。collapse表示12行でもfull hunkをCopyする点は保持する。ただしボタン自体はvirtual feedのrowがmountedのときだけ存在し、画面外任意投稿のglobal commandではない。Issueには
PostKey::Comment(opaque ID)(issue_conversation.rs:26–28,252–259) があるが、それをtargetにする本文Copy actionは実装されていない。PRのflattened Entry (pr_conversation.rs:126–151) はcomment IDを持たず、index-based presentation。存在しないstable-ID buttonを移行済と記載しない。本計画の代替: 目的の投稿まで戻り、mounted TextViewの公式SelectAll/Copyを使う。raw GitHub Markdownではなく、既存PreparedBody→公式Plain-rendered selection→Copyのtrimという経路。外側unmount済の単一投稿をキーボードcommandだけで全文取得する保証は持たない。
投稿全体のCopyボタン新設は、ownerが追加scopeとして望む場合のみ別途設計する(raw Markdown / prepared Markdown / rendered plainのどれをCopyするか、stable full owner identity、画面外targetの選択方法を決める必要あり)。今回は未依頼の新機能として追加しない。hunk Copyを本文Copyと誤表示して代用もしない。
U1 — 最小の公式Textarea accessor(外部prerequisite、選択用U0とは別)
追加調査で公開APIの代案まで確認した。
TextareaState::new(...).rows(1)とTextarea::h(relative(1.))。外側をKagiがpreferred height / min_h(0) / flex_shrink(1)にすれば、現composerと同じ役割分担にできる。InputPresentation(base/input/base/state.rs:564–625) にrows/height/scroll extentなし、631–650はcaret/viewportのみ。scroll_handle/scroll_size(488,496) はcrate-private。display_map()/prewrap()(11836–11865) はEditorMode限定。DisplayMapをもう一個持つ代案自体は可能。しかし source/幅/font変更時に同じ本文を二重wrapping/shapingする。公式wrapperはUnicode break/grapheme・shaped prefix幅を使う (base/input/editor/display_map/text_wrapper.rs:32–111,365–435,525–614)。単なる文字数/GPUI LineWrapper代用は同じ行数を保証せず、12行clampしても第二mapの全logical-line処理量はboundedにならない。不要な計算/コピーと長期のwrap意味同期を持ち込むので採らない。確定するupstream提案: TextareaStateに既存cacheの
wrap_row_count()を返すO(1) getter(提案名visual_row_count等)を追加。frame前/幅変更後のcache更新タイミングをdoc化。別height-policy API/新rendererは不要。upstream変更3ファイル / [INFERENCE] 80–180 LOC:
crates/base/src/input/base/state.rs:11660–11718(小accessor、colocated unit)crates/kit/tests/integration/input/textarea.rs:476–535,1120–1137,1208以降(plain rows1+Kagi式preferred/shrink layout、soft wrap/Unicode/resize/内部scroll)website/component/textarea.md:30–42,86–105(auto-grow minimumとapplication-owned preferred heightの違い)依存・merge条件: officialに正式mergeされたrevをS6で採用する。それまではS0〜S5を進められるが、全要件を保持した最終S6はmergeしない。API名/採用rev/acceptanceは未確定であり、調査中にPRは作成していない。拒否された場合、二重map・仕様縮小・forkを無断採用せずownerに差分を提示する。
S6 — official package family + typed APIs + group撤去の原子的切替
依存: S1/S2/S3/S4/S5-A〜D と U1のofficial取り込み。一つのgreen merge unit。新base/typed APIと依存identityが同時に変わるため、manifestだけ、Findだけ、Radioだけ、旧groupだけという赤い中間commitをdevへmergeしない。PR内の作業/レビューは以下のsubsectionごとに行えるが、mergeは一回。
A. 7 manifest / 一つのGPUI identity / zero patch
Cargo.toml:23,39–51,140–146,181–208crates/kagi-ui-core/Cargo.toml:17–18,36crates/kagi-ui-ecosystem/Cargo.toml:17–18crates/kagi-ui-editor/Cargo.toml:13,18–19crates/kagi-ui-file-history/Cargo.toml:19–20crates/kagi-web/Cargo.toml:20,23–24,31vendor/gpui-terminal/Cargo.toml:49–57既にresolve実証した形(
/tmp/kagi-fork-review/resolve/Cargo.toml:7–14):これがそのままU1を含むという意味ではない。最終revはU1を取り込んだofficial実SHAへ全manifest一斉更新。base Radio/
TextSelection::clearのpublic seamを使うrootには同じofficial revのgpui-baseを直接追加する(component互換facadeに存在しないgpui_component::baseというimportを発明しない)。既存gpui_component::...の多くはofficial component facadeでそのまま残せる。Kit facadeへ全namespaceを機械置換する理由はない。Cargo.lock再生成。S1でcosmic削除済、S6でZed sibling 22 entries(184–205) とcomponent/assets 2 entries(207–208)、空patch headers/commentsを全削除。[profile.dev.package] gpuiはgpui-pre。gui-e2e以外の通常buildでtest-supportが無条件に入らない条件を維持。src/ui/assets.rs:253,262:gpui_component_assets::Assets→gpui_kit_assets::Assets。Root::new(...).font(ui_font())はpublic Styledのfontメソッドで成立するので、Root全面置換は不要(Kagimod.rs:3516,e2e.rs:918,settings_window.rs:35)。B. typed Input / Textarea / Editorの全主要境界
fresh grepでmulti_line builderは4か所(3種類のstate、Commit再生成が2回)、code_editor builderは3か所。single-line InputStateを全置換しない。
src/ui/commit_panel.rs:432,585–605src/ui/operations/commit.rs:108–114/commit_panel_composer.rs:173–181commit_panel_composer.rs:56–61/compact.rs:128commit_panel_composer_render.rs:28–35,87–91src/ui/pr_mode.rs:988–991/mod.rs:1226/pr_page.rs:577src/ui/settings_view.rs:66–68,180,1133/mod.rs:1219src/ui/issues_composer.rs:36,79–82,185,489src/ui/conflict_view.rs:70,1020,1092–1095/mod.rs:1411/conflict_editor.rs:1567crates/kagi-ui-editor/src/lib.rs:276,495,1537–1540,1592,3300src/ui/timeline_row.rs:201–205body_inputを型隠蔽enumにせず、PR Textarea / Issue Editorの薄いrender entryへ分ける。callerは上記2か所。src/ui/issues_composer_e2e.rs:185,289–290,395–398src/ui/window_menu.rs:67–68,131–132/src/ui/render.rs:246–247.read(cx).focus_handle(cx)ではなくpublic.focus_handle(cx)。modal_key_routing.rs:54–55は既にこの形で原則不変。core/input_actions.rstests/recovery/editor_find.rs:6,22ほか,editor_find_hosts.rs:7,14,editor_snapshot_readonly.rs:23,92,menus_ja.rs:144,commit_panel_viewport.rs:188composer replacement sketch(getterはU1の公式確定名に合わせる):
コード全体のcompile済例ではなく、既存composer frame内の置換sketch。3/11/soft-wrapped6行の現oracle(95/239/149px、ADR0134:200–223)と低window内部scrollを受入で照合する。
C. Find / Radio / Select / Theme / Undo
core/find.rs:1–17を既存labels ownerのままKagiの薄いFind UIへ拡張。official Editor.searchable(false)でbuilt-in chromeだけ使わず、公開enginesearch_session()136、set_search_query()172、close_search()186、next_search_match()191、previous_search_match()200、replace_current_search_match()211、replace_all_search_matches()244(base/input/editor/search.rs)を使う。regex/match index/undo backendを二重実装しない。search_labels3builder+set_search_labels3更新を全撤去。cycle_focusはreplace mode時の2入力間だけで、button traversal/labels/help/containmentの代替ではない。tests/recovery/editor_find.rs:22–28,73のfork SearchPanelProbe/help-probeを削除。S4のnative AX /実focus /actual button bounds /hoverで呼ばれたTooltipを使う。official提供のtest queryを使う場合も実Element snapshotであり、名前定数の自己比較にしない。settings_view.rs:699,713,750,965の4groupを 新規src/ui/settings_radio.rsの薄いKagi paintへ。officialgpui_base::RadioGroup/Radioを使い、checked+disabled+AX+pointer/Enter/Spaceはprimitiveへ委譲。indicatorだけ現6/14px dotとprimary_foreground。既存4groupのcallbacksとstable element/focus IDsを維持。base RadioGroupはsemantic containerであり、存在しないautomatic roving-arrow機能を前提にしない。新しい設定storeや独自Radio event systemは作らない。settings_view.rs:503–520のbuilderは.accessibility_label(...)。actual focused ComboBoxのrole/value/openはupstreamが所有。既存ThemeOption::titleのsanitize (99–108)を残し、AXだけ別raw名にしない。custom selection state/open flagを追加しない。core/theme.rs:1271,1277のdirectglobal_mutは新base projection/tokensと乖離する。officialTheme::update(cx, |theme| ...)(component/theme/mod.rs:255–289) を使うか、同等に全projectionを同期。sync_baseだけではtokensはreconcileされないことにも注意。選択active色が同値の毎renderには更新しない(不要clone/refresh-loopを防ぐ)。色変更時のみreconcile+Base sync。全preset/selected/inactive/scrollbar/radio contrastを維持。commit_panel_composer.rs:99,113、issues_composer_e2e.rs:235,255,290等のreplace_allは公式atomic groupingのまま。set_valueへの置換は禁止。smart_replace_undoは前後typingを含めsleep無しでUndo/Redo境界を保持。pr_detail_lifecycle/smart_generation_close_drops_panelのnative release/orphan assertionsを実行して初めて移行完了とする。D. SDK group撤去とKagi既存ownership guardの整理
設計を一つに絞る: SDK
TextSelectionGroupRetirement/ Root group / all-post members / outer edge taskは削除。ただし既存KagiConversationScopeの active/generation はaccepted-row/deferred-render validityにも使われるので残す。これをofficialTextSelectionScopeIdに置き換えない。新しいcross-post model/parser/canonical byte cacheは作らない。issue_conversation.rs:40–55のSDK retirement field/呼出しを除去、:89–95のSDK lease active check、:106,136–144,277–284のgroup作成・publish、weak Root installation/defer部を除去。PostKey/source/retained TextView/geometry/acceptance世代は維持。不要になるsource_revisionは消す。issues_thread.rs:3,135–158のgroup viewport marker/held-edge callbackを削除。TextView237–245は公式の通常selectable/default scope。PR側のtimeline_row::body_markdown/ PR description /review/thread/previewも公式挙動を禁止するscopeを付けない。issue_conversation.rs:342–350,389–395、削除post判定223–225で旧選択をpublicTextViewState::clear_selectionと必要なwindow clearで同期撤去してからtextを更新。clearをstate.updateの内側から同stateへ再入させない。refresh開始/失敗/無関係なpost更新で無条件全clearしない。src/ui/render.rs:690–703の既存Kagi root action境界にcaptureを置き、paint時に捕捉した既存Kagi owner/generationがまだforeground/activeかを現在のapp状態で確認する。staleならpublicgpui_base::TextSelection::clear/必要に応じclear_for_windowで撤去し、そのactionをconsume。validなら一切加工せずofficial descendant/Rootへpropagateする。新しいselection scope/renderer/clipboard formatter/全post scanは作らない。既存retire/activation callersはgeneration ownerとして意図的に維持(API shimではなく既存accepted lifecycle):
github_issue_state.rs:84,231,244,292,322–342;github_issues.rs:158,175,190,249,277;issue_conversation.rs:457,482,496,532,579;issues_composer.rs:260,262;branch_cleanup.rs:49;command_palette.rs:332;ecosystem.rs:90;file_history.rs:241;overlay_focus.rs:366,410;pr_mode.rs:264;tabs.rs:217;workspace_mode.rs:635,745;operations/commit.rs:900;operations/conflict_detect.rs:288;operations/modal_state/arbitration.rs:48,66。この設計ならpure/no-cx modal setters全体にAppを注入する大改造は不要。
depart_active_tab(tabs.rs:204, callers267,399,home.rs:85,111)、leave_takeovers(workspace_mode.rs:621, callers673,680,707,727,760)、leave_settings_for_overlay(overlay_focus.rs:365, callersabout.rs:14,commands.rs:2662,2674) も既存generation失効の入口として残す。tab_view.rs:458–464,593–595/tab_ui_state_ops.rs:148–150のKagi lifetime holderとgithub_issue_state.rs:322–342のコメントはaccepted-generation guardという用途に明確化し、SDK group storageとしては残さない。S3の13旧scenario改訂と5新規positive scenario、ADR0198/0136/0200/0142・decisions・CHANGELOG・verify skillを同mergeで切替。未変更のretirement callerを「SDK group caller取り残し」と誤判定しない一方、SDK型/import/methodのliteralがproduction/testに残らないことは確認する。
S6 regressionの先行順・最終gate
commit_panel_viewportの3/11/soft-wrapped6行、native height95/239/149px、低window・high zoomで内部wheel/入力/Commit footerを維持。font shapingをmockしない。S6の絞込みgate(全部実在するprefix、括弧の新規scenarioを除く):
issue_conversation_,pr_conversation_post_select_all,pr_thread_post_select_all,markdown_preparation_edges,workspace_mode_toolbar,pr_same_number,pr_threads,review_hunk_full,issue_body_zoom,graph_copy_foreground,graph_pointer_focus,editor_,conflict_,settings_,sidebar_search_,input_names_inventory,input_ime_escape_boundaries,commit_panel_,smart_replace_undo,smart_generation_,pr_detail_lifecycle,pr_diff_extent_,diff_keyboard_selection,split_diff_keyboard_order,diff_selection_revision,terminal_,status_bar_,finder_scale_rest,inspector_scale,oplog_detail_select_copy新規は S3/S4節に列挙したscenarioだけ。S6はframework family全体が変わるため、絞込みだけで完了せず、通常の全native suite(filter無し、KEEP_GOING無し)と別実行のinventory smoke/比較、workspace testsを通す。inventoryとordinary filterは混ぜない(verify skill:2702–2706)。
platform/dependency gate:
scripts/build-web.shと既存web harness。macOS上の無理なcross-link失敗を無視して全platform成功としない。cargo metadata/cargo tree --target all --edges normal,build -i gpui-preでactive GPUI identityが一つ、root/crate/CLI-configにpatch/replace/local-forkの隠し経路がないこと。optionalな非activegpui-fastmetadata nodeの存在だけをduplicate active dependencyと誤認しない。[patch]ゼロ、lockにTomiXRM/zed・cosmic・gpui-kit fork sourceゼロ、official component/base/assets rev一致。pathのowned terminalは認めた例外として明示。S6 全変更予定パス(distinct 60)
以下は上記設計で変更するパスを数えたもの。新しいAPIを導入するために無関係な全InputState参照を機械変更する一覧ではない。compile未実施なのでcompiler-drivenな小差分の予備は後述に別計上する。
src/ui/issue_conversation.rssrc/ui/issues_thread.rssrc/ui/render.rstests/recovery/issue_conversation_selection.rstests/recovery/issue_conversation_lifecycle.rstests/recovery/issue_enabled_input_copy.rstests/recovery/issue_offscreen_select_all.rstests/recovery/issue_conversation_support.rstests/gui_e2e_runner.rstests/recovery/conversation_official_selection.rs(新規)docs/adr/0198-read-only-issues-workspace.mddocs/adr/0136-pr-merge-and-review-conversation.mddocs/adr/0200-pr-workspace-layout.mddocs/adr/0142-markdown-image-rendering.mddocs/decisions.mdCHANGELOG.md.claude/skills/verify/SKILL.mdsrc/ui/github_issue_state.rssrc/ui/tab_view.rssrc/ui/tab_ui_state_ops.rsCargo.tomlCargo.lockcrates/kagi-ui-core/Cargo.tomlcrates/kagi-ui-ecosystem/Cargo.tomlcrates/kagi-ui-editor/Cargo.tomlcrates/kagi-ui-file-history/Cargo.tomlcrates/kagi-web/Cargo.tomlvendor/gpui-terminal/Cargo.tomlsrc/ui/assets.rscrates/kagi-ui-core/src/theme.rscrates/kagi-ui-core/src/find.rscrates/kagi-ui-core/src/input_actions.rssrc/ui/commit_panel.rssrc/ui/commit_panel_composer.rssrc/ui/commit_panel_composer_render.rssrc/ui/compact.rssrc/ui/mod.rssrc/ui/settings_view.rssrc/ui/conflict_view.rssrc/ui/conflict_editor.rscrates/kagi-ui-editor/src/lib.rssrc/ui/window_menu.rssrc/ui/settings_radio.rs(新規)tests/recovery/editor_find.rstests/recovery/editor_find_hosts.rstests/recovery/editor_snapshot_readonly.rstests/recovery/menus_ja.rstests/recovery/commit_panel_viewport.rsdocs/adr/0001-gpui.mddocs/adr/0006-gpui-component.mddocs/adr/0080-settings-window.mddocs/adr/0134-simpler-commit-pane.mddocs/adr/0125-apple-hig-themes.mddocs/adr/0222-diff-list-estimated-height.mdsrc/ui/timeline_row.rssrc/ui/pr_mode.rssrc/ui/pr_page.rssrc/ui/issues_composer.rssrc/ui/issues_composer_e2e.rssrc/ui/operations/commit.rsS6 churn見積り:
[INFERENCE]4,200–6,900 LOC(typed API/Find/Radio/theme/assets/selection/tests/docs、generated lock除外)。そのうちgroup撤去/既存guard整理/oracle改訂は約1,200–2,000 LOC。旧案の38-file App-aware setter全面移行は採らず、既存Kagi validity tokenを実action境界で検証するため、no-cx modal setter chainの100か所規模の引数変更をscopeへ持ち込まない。別途、未コンパイルの公式API差分に対する予備 0–20 files / 400–900 LOC を想定する。この予備を確定変更・検証済として数えない。最大リスク: U1のupstream accept待ち、typed APIを一つでも残すこと、Rootのaction propagation/Fallbackとretired ownerの関係、official Markdown/Copyの分離記号/trimを旧fork canonical streamと同一視すること、Theme base/tokensの同期漏れ。U0や独自rich rendererの再導入は解決策にしない。
仕上がりの順序・merge単位・依存関係
推奨Kagi merge順は S0 → S2 → S1 → S3 → S4 → S5-A → S5-B → S5-C → S5-D → S6。U1はS0後すぐ別upstream作業として進められるが、取り込みまでKagiは現pinのgreenを保つ。S5-CはA/Bとは独立して先行可能。shared file編集はmerge/rebaseで一人が統合し、別々に半分のAPIをmergeしない。
S6以外の各unitは現依存pinで独立greenにできる範囲だけを採る。S3の「準備」とS6の「保証撤去+official化」を区別してchangelogに記す。S5-Dはまだ旧group consumerを維持し、S6で最終撤去する。移行完了後に旧path/flag/aliasを残す計画ではない。
未確認事項・完了を妨げる条件
調査の変更範囲: Kagi本体/worktreeへの変更ゼロ、commit/push/PR/issue commentなし。ここで新規作成したdeliverableはこの
local://fork-review-1479-plan.mdだけ。実行済の数値は前報のthrowaway list measurementを出典付きで再利用し、今回のplanning中にbuild/test/formatterは実行していない。Merge pull request #1585 from TomiXRM/deps/s0-upstream-policy
Merge pull request #1586 from TomiXRM/deps/s2-terminal-owned
Merge pull request #1587 from TomiXRM/deps/s3-post-selection
Merge pull request #1589 from TomiXRM/deps/s5a-pr-lane-uniform
Merge pull request #1592 from TomiXRM/deps/s5c-variable-geometry
Merge pull request #1594 from TomiXRM/deps/s1-cjk-fallback
Merge pull request #1596 from TomiXRM/deps/s5d-issue-geometry
Merge pull request #1597 from TomiXRM/deps/s4-input-chrome
Merge pull request #1600 from TomiXRM/deps/s5b-inspector-uniform
Merge pull request #1613 from TomiXRM/deps/s1b-cosmic-patch-removal
1 remaining item
TomiXRM commentedon Oct 16, 2026
U1: TextareaState::visual_row_count() の最小公開APIを実装しました。既存cacheのO(1)参照のみで、追加のshaping/copy/cacheはありません。公式upstreamへの提出用commit: TomiXRM/gpui-kit@d24685a(Kagiの依存先には採用しません)。base unit 1,375件、Kit integration/UI 395件、Clippy、fmtを通過。macOSのnative text system + Metalによる使い捨てsmokeでも3/6/20行、120pxへの縮小、内部scroll、Unicodeの幅変更を確認しました。添付はそのnative描画です。公式repoはREAD権限で画像添付が拒否されたため、ここに検証画像を保存します。S6は公式merge後のrevisionを採用する方針を維持します。
TomiXRM commentedon Oct 16, 2026
#1479-S6 進捗 — official U1 merge 待ち
不足していた cached visual-row API は公式 longbridge/gpui-kit#3540 に提出しました。PR は OPEN / MERGEABLE、macOS・Linux・Windows を含む upstream CI は通過しています。提出用 fork branch を Kagi の依存には使っていません。
Kagi は隔離 worktree
deps/s6-official-cutoverで作業中です。まだ push / PR / merge していません。[patch]を撤去。全 target の normal/build dependency closure は activegpui-pre =0.3.9が1系統、TomiXRM fork source は0、component/base/assets は同一 official revision。cargo buildはTextareaState::visual_row_count()が未公開の1エラーまで到達。公式PRが merge された revision に一括更新するまで、Kagi全体を green と扱いません。kagi-ui-corelibrary: 309 passed / 9 ignored。kagi-ui-editorlibrary: 42 passed。cargo fmt --all --check、両 UI crate の Clippy、uv run --project ci check-all通過。Clippy は既存箇所の警告あり。is_open/ renderer-fed option recorder を廃止し、実際の native AX tree を読むテストへ移行。全体の native runner は未実行です。公式依存のみの core 描画スモーク
変更した
kagi-ui-coreを一時プログラムから実行し、macOS の実 text system + Metal で EN/JA × Apple Light/Dark の4条件を描画。Cmd-F →alphaの3件一致 → Replace all → Escape → 1回の Undo で元の日本語を含む本文に戻ることを確認しました。一時ソースは削除済みです。これは offscreen core smoke であり、Kagi全体の Tier A/B、OS AX・VoiceOver・IME の証明ではありません。
次は official #3540 merge → component/base/assets の同時 revision 更新 → Kagi全体の build・workspace tests・native/各platform gate → greenなS6 PRです。fork fallback や local SDK patch は追加しません。
TomiXRM commentedon Oct 16, 2026
Textarea 上流APIの修正版とネイティブ描画証跡
上流の #3540 はメンテナー判断で閉じられました。専用の
visual_row_count()は追加せず、指定された既存のdisplay_map()/prewrap()を全入力モードに共通化する修正版を準備しました。検証したコミット:
6a8f0ebb81a12d4095371e8844cfeb112458f109test-support有効)。Kit integration/UI: 400 passed。cargo fmt --all -- --check、cargo build -p gpui-base -p gpui-kit、対象 crate の Clippy が成功。依存block v0.1.6の既存 future-incompatibility notice あり。prewrap、内容に応じた preferred height、120px の制約下での縮小・内部スクロール・キャレット可視性、日本語/結合文字/家族絵文字の折り返し、初回レイアウト後のprewrapno-op を確認。下記画像は今回の修正版から取得しました。test-supportなし)は、この checkout のa11y_disabled.rs:298が feature-gated なcrate::test_supportを参照して停止したため、必要な feature を有効にして実行しています。無関係なテストの修正は混ぜていません。これは Kagi 全体の依存切り替え完了や、OS の IME/Accessibility 検証を意味しません。公式上流への取り込みを待ち、その revision で Kagi の依存と呼び出し側を切り替えます。投稿用 fork を Kagi の依存先にはしません。
TomiXRM commentedon Oct 16, 2026
修正版を公式 upstream の longbridge/gpui-kit#3564 として提出しました。longbridge/gpui-kit#3564
Textarea の前提となる追跡先は、閉じられた #3540 ではなく #3564 の公式取り込みです。検証済み head は
6a8f0ebb81a12d4095371e8844cfeb112458f109。新設 getter ではなく共通のdisplay_map().wrap_row_count()/prewrap()を使用する方針で、直前のコメントに native Metal の画像とローカル検証結果を添付しました。上流 CI は実行中です。S6 は引き続き未完了です。投稿用 fork を依存先にする回避は行わず、公式 merge 後に採用 revision・Kagi の呼び出し側・全 platform/IME 等の受入 gate をまとめて確認します。
TomiXRM commentedon Oct 16, 2026
公式 upstream #3564 の head
6a8f0ebb81a12d4095371e8844cfeb112458f109は、CI と Docs build の 16 checks がすべて成功しました。macOS/Linux/Windows tests、GPUI Fast の各 platform/WASM、Shell Core/Components/Standard Runtime、Lint、no-default-features を含みます。PR は OPEN、レビューコメントは現時点でなく、残る外部待ちはメンテナーのレビュー/公式 merge です。これは Kagi 本体の S6 や Windows/Linux GUI・日本語 IME の受入完了ではありません。公式取り込み前に投稿用 fork へ切り替えることはしません。
TomiXRM commentedon Oct 18, 2026
S6: Find の置換対象退行を公式 SDK 側で修正
公式 SDK 移行中に、後方の一致箇所を表示してから新しい検索語を入力すると、即時の Replace が先頭の画面外の一致箇所を変更する問題を再現しました。大小文字の切替でも同様です。
公式 PR: longbridge/gpui-kit#3584
S6 全体はまだ未完了です。 公式 PR のマージ・採用、Find の実キーボードフォーカスと AX フォーカスの紐付けの調査、残りの native / platform ゲートを継続しています。Kagi の依存を contribution fork に向けることはしません。
以下は公式修正後の置換操作の前後です。修正前バイナリの比較画像ではありません。置換後は比較用に表示位置だけを元へ戻しています。OS ウィンドウ/AX/IME の検証証拠とは区別しています。
TomiXRM commentedon Oct 18, 2026
S6 検証の進捗(まだ切り替え完了ではありません)
公式依存だけを使う候補
gpui-kit@2062380587752a0095b4f49ca870cfde54d701c6/gpui-pre =0.3.9で追加検証しました。fork pin・path patch は追加していません。通過したもの
cargo test --workspace: 3,543 passed / 48 ignored。commit_panel_viewportとsmart_replace_undo: 実 native runner で 2/2 PASS。composer の3行・11行・折り返し6行は引き続き 95 / 239 / 149px。低いウィンドウ・高倍率での内部スクロール、編集、footer の到達性を維持しています。公式側へ提出した修正
残っている受入
公式merge後の同一revisionへの一括採用、native AX/Find/compact Popoverの再検証、安全な明示filterによる全native suite、残りのplatform/web/inventory受入が必要です。S6を完了・リリース可能とは扱っていません。
添付はLinuxの8条件と、実macOSウィンドウでのSource Editor / Conflict Resultの100%→170%比較です。見た目も確認をお願いします。
TomiXRM commentedon Oct 18, 2026
S6追加検証:Linuxの独立オーバーレイとCopyの所有者境界
S6は未完了です。未マージの上流修正をKagiの依存先に採用していません。 以下は追加で実行・目視確認した範囲です。
Linux:独立した描画ルートでもフォントが保たれること
X11 / Wayland × JA / EN × 最小フォント環境 / システムフォント環境で、実際の描画とネイティブのフォント選択を確認しました。
24枚を個別に確認しました。以下は対象部分を元ピクセルから切り出した一覧です。Dragは毎回、独立した検証用display内で実際にpointerを移動させ、前のケースの位置に残ったghostを誤って撮らないようにしています。ユーザーのデスクトップやポインターは操作していません。
負の対照も確認済みです。 各ルートの明示的なUIフォント指定をLinuxの検証用コピーで一時的に外すと、Tooltip / Drag / Worktreeの3ケースすべてが期待する日本語フォントのidentity検査で失敗しました。元のソースへ戻して再ビルドすると3ケースとも成功し、復元後の3ファイルは元のSHA-256と一致しました。復元時のmtimeにより一度古いCargo成果物が再利用されたため、その結果は採用せず、復元済みソースを再コンパイルした結果を使用しています。
Linux上のTooltipの既存4回帰テストも通過しました。検証用アプリ・display servicesは停止し、一時exampleは保存済み証拠を残して削除しました。このLinuxコピーは先のLinux本体8条件と同じ移行スナップショットです。後述の最新macOS Copy修正までLinuxで検証した、という主張ではありません。
macOS:現在の画面だけがCopyを受け取ること
残る公式SDK側の前提条件
新たに longbridge/gpui-kit#3588 を提出しました。保持していたTextViewをunmount→remountすると古い選択が復活する問題を、既存の選択終了処理で修正します。独立レビュー、失敗→成功の回帰、実AppKit/Metalの部分選択とSelect All、Base 1,413 / Component 702 / Kit 400 / UI 1テスト、実Metal 13チェック、fmt / Clippyを通過し、push後のremote SHAも一致しています。Kagiへの採用は公式merge後です。
もう一つの「ドラッグ直後、次のpaintより前にCopyすると古い範囲がコピーされる」問題は修正中です。最初の案はレビューでカスタム要素・スクロール座標・仮想化した端点への回帰が見つかったため採用していません。180段落の同一update内Copy要件は削除していません。
既存の上流前提条件は gpui-kit#3583 / #3584 / #3585 / #3586。#3586はTest Docsのみ通過が確認できており、Rust CI全通過とは扱っていません。公式merge後の依存同期、全native suite、現在の実画面、Windows実target、WASM / web harnessなどが残っています。
スクリーンショットの日本語の読みやすさと、独立オーバーレイで本文とフォントが揃っているかも見ていただけると助かります。エージェントの目視確認とは別に、人間の確認結果はまだ受領していません。
TomiXRM commentedon Oct 18, 2026
S6追加進捗:同一update内Copyの上流修正とWASM実行
gpui-kit#3589 を提出しました。ドラッグ直後、次のpaintより前のCopyが古い範囲を返す問題への修正です。
最初の修正案は採用せず、既存のcompleted-frame情報とAST所有の選択範囲を利用する形に変更しました。カスタムinline/block、仮想化で消えた部分選択の端点、スクロール直後の座標、source置換を維持し、Copyのために再描画・focus変更・別の選択registryを追加していません。レビューで見つかったnative line配列の二重収集も除去しました。
c27abc336552128890e76b5ba4c492f9e119abecと一致。再現用native consumerの全ソースはPR本文にあります。Kagi側の現在の通常gate
最新の会話Copy capture修正を含むmacOS default build、workspace 3,543テスト成功・48 ignored、fmt、workspace Clippy、
uv run --project ci check-allが通過しました。これは全native GUIシナリオの完了を意味しません。Settingsを含む残りのnative検証は、明示filterを付けた小分けのプロセスで継続中です。WASM / 実ブラウザ
既存の
scripts/build-web.shが nightly 1.101.0 / wasm-bindgen 0.2.123 で成功しました。実ChromiumでBrowserWebGpuを選択し、既存catalogを起動。1280→900 logical px、20px刻みの全20条件で、最初のresize frameと200ms後のPNGが一致し、page errorはありませんでした。Retinaの実DPR 2とcanvasの物理サイズが一致した状態で検証しています。初期のCDP DPR-1試行はdevice-pixel-content-boxとの不整合があったため採用していません。以下の両画像は目視済みです。既存Playwright specの起動・resize操作をbrowserから直接実行した結果で、Playwright suite自体の実行結果ではありません。また、この既存web harnessはpure-domainの描画catalogであり、Git backendやKagi全体をWASMで動かしたという意味ではありません。
検証用browserとHTTP serverは停止済みです。上流PRのbranchやフォークへの依存差し替えはしていません。前の追加検証の #3588 と今回の #3589 を含め、公式merge後の依存同期・全native受け入れ・Windows実target compileなどが残っており、S6はまだ完了扱いにしていません。
TomiXRM commentedon Oct 18, 2026
S6 追加検証:通常 Preview Copy・Settings スクロールバー
S6 の移行完了報告ではありません。Kagi の依存は引き続き公式
2062380587752a0095b4f49ca870cfde54d701c6。下流 fork・patch・contribution branch の pin は追加していません。通常 Reply Preview の実操作
隔離した通常ビルドの実ウィンドウで、未送信の
**S6_NATIVE_PREVIEW_20261018**を Preview に切り替え、本文クリック → Cmd+A → Cmd+C。クリップボードは Markdown 記号なしのS6_NATIVE_PREVIEW_20261018と完全一致しました。元のクリップボードは全 representation を復元し、未送信 draft を消去して正常終了。GitHub への投稿は行っていません。日本語 IME の証明ではありません。常時表示バーの予約レーン修正
非 auto-hide 時の 16 logical px の予約レーンを、新 SDK の既定 handle viewport が含まないため、thumb が 16 px 内側に描かれていました。既存 wrapper の persistent 分岐だけに公式
viewport_from_layout()を指定しています。同じ 715×1064 logical px の実 Settings ウィンドウで比較しました。修正前は予約レーン x=707 のドラッグが効かず、内側 x=691 の thumb なら移動。修正後は x=707 で末尾の「Lock worktree while terminal is open」まで到達します。テスト座標・期待値は緩和していません。
実画面で EN/JA × 100%/167% の4条件を確認済み。auto-hide は wheel による表示・移動、thumb ドラッグ、バー外の非操作領域をドラッグした後の消失を確認しました。単純な PID-targeted MouseMoved だけでは thumb が残ったため、その入力経路を fade 成功の証拠にはしていません。SDK の不具合とも断定していません。
スクロールバーの OS 設定は固有 bundle ID
dev.kagi.agent.scrollbar.20261018の domain にだけ設定し、ユーザーの global 設定は変更していません。検証プロセスはすべて exit 0、専用 preference domain も削除しました。Gate と残件
cargo fmt --all --check、cargo build --locked、cargo test --workspace --locked、cargo clippy --workspace --locked、uv run --project ci check-allPASS。workspace は 3,543 passed / 48 ignored。Clippy の既存 warning は残っています。公式 prerequisite の merge/adoption、残りの safely batched native acceptance、同期済みの各 platform gate、Windows 実ターゲット compile は未完です。現在の成果を S6 全体の受入完了とはしません。
スクリーンショットの配置・読みやすさについて、人の目でも確認をお願いします。
TomiXRM commentedon Oct 18, 2026
S6 — Git 2.39修正統合後のmacOS / Linux全workspace検証
独立した互換性修正 #1658 は
9245159371cfc3c0a281c3547a1db217b4d2e730でmainへmerge済み、#1657はclose済みです。その4ファイルの修正を、未commitのS6移行作業版へ取り込みました。SDK pinは公式2062380587752a0095b4f49ca870cfde54d701c6のままです。新しいfork pin / patch / shimは追加していません。check-all成功。既存のClippy警告あり。--versionはkagi 0.46.0。Linuxは同一の統合ソースに対する
cargo test --workspace --lockedの結果です。以前の「S6非backend 2,188件」と「独立branch backend 1,345件」を足して合格扱いしたものではありません。1 build job、tini -s、非root verifierで実行し、rootのpermission bypassやPID 1のzombie retentionを製品の挙動と混同していません。追加のnative検証では、Branch picker / Activity / Inspector / bottom panelの34シナリオが成功しています。Activityは言語・theme・zoomとnative resizeをsettleさせてから180pxのpaneを開き、hover/keyboardのthree-pane検証はdesktop幅を整数logical pixelで指定するようfixtureを修正しました。既存のAX・localization・paint assertionsは維持しています。先のSettings等37シナリオと、今回のplatform workspace検証は別の実行記録です。
通常ビルドのJA Activity画面も確認済みですが、これはVoiceOver検証ではありません。先のPreview Copy / scrollbar実画面記録も、workspace testsで代替したとは扱っていません。canonical verification skillとmigration documentに再現条件・適用範囲を追記済みです(skill 更新済み)。
まだ完了ではない範囲
公式GPUI Kit #3583–#3586 / #3588–#3590は、今回のAPI照会でも全て OPEN です。contribution branchはKagiから参照していません。公式merge後の依存更新、残りのnative採用ゲート、Windows実target等の最終同期検証が必要です。S6自体をcommit / push / merge / releaseしたとは主張しません。
TomiXRM commentedon Oct 19, 2026
2026-10-19 — 公式 prerequisite 2件が merge 済み
上流の実際のPR状態とcheck結果を再確認しました。
af456ddb2fe7bc57f040bb94064135cf353488d6。57aff71ef446e9cbd702a0e462c5813e2fd9a7b7。maintainer更新後の各headについて、Rustを含む19 checksがすべてSUCCESSです。macOS / Linux / WindowsのTest、GPUI Fast、WebView、Shell Runtime等を含みます。以前の「Test Docsのみ/Rust CI未確認」という保留状態は、この2件について解消しました。これは上流SDKのCIであり、KagiのWindows実行検証を済ませたという意味ではありません。
残る公式 prerequisite は #3583・#3584・#3585・#3588・#3589 の5件で、現在もOPENです。S6の最終公式依存採用・同期済みnative/platform受入は引き続き未完です。contribution branchや追加forkへのpin差し替えは行いません。今回のMarkdown/PDF/branch-copy PR #1664も依存cutoverとは分離しており、既存GPUI pinを変更していません。
input: anchor custom search queries to the viewport (#3584)
TomiXRM commentedon Oct 19, 2026
前提 PR の状態を公式 API で再確認しました。#3583(caret reveal、11:25Z)、#3584(Find anchoring、11:27Z)、#3588(selection unmount、11:40Z)、#3589(同期 rich Copy、11:44Z)が 2026-10-19 に MERGED です。既に取り込まれた #3586 / #3590 と合わせ、残る未マージの前提は longbridge/gpui-kit#3585(native input accessibility focus ownership、head cbde718bf9cc95e75ebe8e30506daf04ecaa820c)です。#3585 に新しい review / issue comment はありません。今回確認した4件には更新後の CI がまだ QUEUED / IN_PROGRESS のものがあり、全チェック完了とは扱っていません。
公式 main は d476c7635ce03e4100cec67b6cac88014cb79de0。S6 は引き続き公式への一括 cutover と Kagi 側の native / platform / dependency acceptance が必要です。新しい fork pin や contribution branch pin は追加していません。
関連する #1275 は現行 pin と公式公開 API の差分を再調査しました。公式には TextViewState::list_state()/bounds() があるため、S6 後は同一 selection/scroll owner のまま gutter geometry を検討する経路があります。no-wrap/horizontal scrolling と Copy/性能の受入条件は未完了です。通常 GUI の JA Dark 167% の確認結果: #1275 (comment)
TomiXRM commentedon Oct 19, 2026
公式移行の残件 #3585 に対する maintainer review を修正し、同じ PR に追加コミットを push しました。
longbridge/gpui-kit#3585 (comment)
common::input/input_inを削除。fixture の accessible name と明示的な scoped/named query に移行。945ff36718e1f22f56142185dfc7efa3834d3fe7と contribution remote の一致を確認。再レビューはコメントで依頼済みです(GitHub の正式な review 再依頼 API は権限不足)。現在は更新 head の CI と maintainer 再レビュー待ちです。Kagi 本体の依存 pin はまだ変更していません。既存の S6 前提は引き続き #3585 の公式 merge と採用後の受入検証です。
別途、PDF 境界操作で発見した disabled Button の Tab 停止は、承認済みの公式 PR #3606 / Kagi #1677 で追跡しています。