Skip to content

fix: マウスレポート有効化で TUI のホイールスクロールを確実に止める#73

Merged
kkyosuke merged 2 commits into
mainfrom
fix/tui-mouse-wheel-scroll
Jun 14, 2026
Merged

fix: マウスレポート有効化で TUI のホイールスクロールを確実に止める#73
kkyosuke merged 2 commits into
mainfrom
fix/tui-mouse-wheel-scroll

Conversation

@kkyosuke

Copy link
Copy Markdown
Owner

目的

#72 で代替スクロールモード(DECSET 1007)を無効化したが、Apple Terminal.app など 1007 を無視する端末ではマウスホイールが依然として↑↓矢印に合成され、usagi のリスト移動や埋め込みシェルへの転送として「スクロールが効いてしまう」問題が残っていた。これを端末非依存に解消する。

原因

DECSET 1007 は xterm 拡張で、Apple Terminal.app などは解釈しない。そのため代替スクリーン中もホイールが矢印キーに変換され、リスト・埋め込みシェルへ漏れていた。

変更内容

  • src/presentation/tui/screen.rs
    • AlternateScreenGuard で代替スクリーン突入時にマウスレポート(DECSET 1000 + SGR 1006)を有効化 (\x1b[?1000h\x1b[?1006h)、Drop で解除。1007 の無効化も defensive に残置。
    • ホイールが端末側スクロール/矢印合成ではなく「マウスイベント」として届くようになり、端末自身がスクロールしなくなる。1007 を無視する端末でも有効。
  • src/presentation/tui/term_reader.rs
    • マウスレポート有効化で stdin に流れ込むマウス列を console がデコードできず後続バイトが文字キーとして漏れる問題に対し、next_non_mouse_keySGR (ESC[<…M|m) / X10 (ESC[M+3byte) 両形式をまとめて破棄し、次の実キーだけを返すフィルタを追加。
    • フィルタ本体は純粋関数として単体テスト(7 ケース)。
  • 埋め込みターミナル(terminal_pane.rs)は crossterm が Event::Mouse を返し Event::Key 以外を元から破棄するため追加対応不要。同じガード変更で矢印転送の漏れも解消。
  • ドキュメント更新: document/design/01-welcome.md / document/03-commands/02-tui.md / issues/006-terminal.md

副作用: TUI 起動中は Terminal.app のマウス選択挙動が変わる(テキスト選択に Option キーが必要)。これはマウスレポート有効化のトレードオフ。

テスト・確認方法

  • cargo fmt / cargo clippy --all-targets -- -D warnings / cargo test(648 passed)をパス。
  • カバレッジ enforce(line/function 100%)を pre-commit(lefthook)で通過。
  • 手動確認推奨: Apple Terminal.app で usagi TUI 起動中にホイール操作 → メニュー・リスト・埋め込みターミナルがスクロールせず、終了後は端末のスクロール挙動が元に戻ること。

🤖 Generated with Claude Code

DECSET 1007 を無視する Apple Terminal.app などでホイールがリスト・
埋め込みシェルへ漏れる問題を、マウスレポート(1000+SGR 1006)有効化と
入力フィルタで解消する。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kkyosuke kkyosuke force-pushed the fix/tui-mouse-wheel-scroll branch from bf63d4a to a05d79d Compare June 14, 2026 20:22
@github-actions

Copy link
Copy Markdown

📊 Test Coverage

🚀 いまのカバレッジ (Lines): 100.00% — この調子でいこう!

🎉✨ パーフェクト!全ファイル Lines カバレッジ 100% を達成しました 🏆🐰

@kkyosuke kkyosuke merged commit 4afeef3 into main Jun 14, 2026
4 checks passed
@kkyosuke kkyosuke deleted the fix/tui-mouse-wheel-scroll branch June 14, 2026 20:45
kkyosuke added a commit that referenced this pull request Jun 14, 2026
## 目的

#73 でマウスレポート(DECSET 1000 + SGR 1006)を有効化し、Apple Terminal.app
でホスト端末のスクロールバックが動く問題は解消した。しかし**マウスレポート列がそのまま画面に大量表示される**新たな問題が残っていたため、これを解消する。

```
^[[<64;70;23M^[[<64;70;23M^[[<64;70;23M...
```

## 原因

`console` は**キー1個ごとに raw モードを on/off** し、読み取りの合間は元の cooked / `ECHO`
有効モードに戻す。マウスレポート有効化後、ホイール連打で stdin
に流れ込むレポート列が、その合間に**カーネルの端末エコーでそのまま画面へ出力**されていた(先頭の `^[` は ESC
のエコー)。`term_reader` のフィルタはキー列の漏れは防げるが、読み取りの“間”に起きるカーネルエコーは防げない。

## 変更内容

- `src/presentation/tui/echo.rs`(新規): TUI 表示中だけ端末エコー(`termios` の
`ECHO`)を切る薄い RAII `EchoGuard`。unix のみ実装、非 unix は no-op。出力処理 `OPOST`
は維持するため `\n`→`\r\n` のレンダリングは不変。
- `src/presentation/tui/screen.rs`: `AlternateScreenGuard` が `EchoGuard`
を保持し、TUI ライフタイム全体でエコーを抑止(Drop で復帰)。
- `Cargo.toml`: unix 限定で `libc`
依存を追加(`[target.'cfg(unix)'.dependencies]`)。
- `scripts/coverage.sh`: 端末 I/O の薄いラッパとして `tui/echo.rs` をカバレッジ計測対象外に追加。
- ドキュメント更新: `document/design/01-welcome.md` / `issues/006-terminal.md`。

## テスト・確認方法

- `cargo fmt --check` / `cargo clippy --all-targets -- -D warnings` /
`cargo test`(669 passed)をパス。
- カバレッジ enforce(line/function 100%)通過。
- 手動確認推奨: Apple Terminal.app で usagi TUI 起動中にホイールを連打 →
メニュー・リスト画面にマウスレポート列が**出力されない**こと、スクロールも起きないこと、終了後に端末のエコー/スクロール挙動が元へ戻ること。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: test <test@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kkyosuke added a commit that referenced this pull request Jun 21, 2026
## 目的

error-log PR #236(TUI のセッション失敗をエラーログに記録)のレビューで判明した「まだ捕捉できないエラー」を、usagi の
issue ストア(`.usagi/issues/`)へ task として登録する。

## 変更内容

新規 issue 4 件を追加(コードは変更しない):

| # | 種別 | 優先度 | 内容 |
|---|---|---|---|
| #70 | fix(pty) | high | エージェント/シェルの異常終了(非ゼロ
exit)を記録。起動成功**後**のランタイム失敗を拾う。「session が失敗した」の本命経路。 |
| #71 | refactor(tui) | medium | エラー記録を単一シンクへ集約し、画面内ログとファイル永続化を 1
経路に。「画面に出る=ファイルに残る」へ。 |
| #72 | feat(mcp) | medium | MCP 経由の `session_create` 失敗も記録。 |
| #73 | fix(tui) | low | 監視スレッドの異常終了(mutex poison での `break`)・ワーカースレッドの
panic を記録。 |

いずれも #236 の続きで、現状「起動失敗は拾えるが起動後の失敗・MCP
経路・バックグラウンドスレッドの異常は拾えない」という穴を埋めるもの。

## テスト・確認方法

- issue ファイルのみの追加(`.rs` 変更なし)。frontmatter は既存
issue(番号・status・priority・labels・dependson・related)に準拠。
- `index.json` は gitignore 済み(スキャンで再生成)のため対象外。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: test <test@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kkyosuke added a commit that referenced this pull request Jun 21, 2026
## 目的

issue
#73。バックグラウンドスレッドで起きる致命的失敗が握り潰され、エラーログに残らない問題を修正する。監視スレッドやワーカーが静かに死ぬとセッション表示が事実上機能停止するのに、原因を後から追えなかった。

## 変更内容

- **ワーカースレッドの panic 捕捉**: セッション作成・削除のワーカー本体を `catch_unwind`
で包む(`complete_or_record_panic`)。panic すると `complete`
を呼ばずスレッドが死に、タスク行が回り続け op-lock も poison したまま痕跡が残らなかった。panic ペイロードを
`ErrorLog::record` で記録し、タスク行を失敗として確定するようにした。
- **監視スレッドの異常停止記録**: 埋め込みターミナル監視スレッドが共有状態の mutex poison
で停止する際、停止理由を記録してから `break` する(以後 bell / phase バッジが静かに凍結するのを追跡可能にする)。
- **記録判断ロジックの集約**: panic ペイロードの復号・ログ行とタスク行メッセージの整形を、カバレッジ対象の `tasks.rs`
に `panic_outcome` として寄せた(カバレッジ除外の `home/mod.rs` からはスレッド spawn と unwind
境界のみ)。
- 通知失敗・resize 失敗などの non-fatal な握り潰しは対象外(ノイズを増やさない)。
- `document/data/01-global.md` のエラーログ仕様にバックグラウンドスレッド異常終了の記録対象を追記。

## テスト・確認方法

- `tasks.rs` に `panic_outcome` のテストを追加(`&str` / `String` / 不透明ペイロードの 3
系統、create / remove 双方の文言)。
- `cargo fmt` / `cargo clippy --all-targets -- -D warnings` / `cargo
test` 通過。
- カバレッジ 100%(line / function)維持を `scripts/coverage.sh` で確認。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: test <test@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kkyosuke added a commit that referenced this pull request Jun 21, 2026
## 目的

エラーログ拡充(#70-#73)の実装が `main` にマージ済みなのに、対応する issue の `status` が `todo` /
`in-progress` のまま残っていたため、実態に合わせて `done` へ更新する。

## 変更内容

以下 4 issue の `status` を `done` に更新(`updated_at` も更新)。

| issue | 内容 | 実装コミット |
|---|---|---|
| #70 | fix(pty): エージェント/シェルの異常終了をエラーログに記録 | `#239` |
| #71 | refactor(tui): エラー記録を単一シンクへ集約・永続化 | `#244` |
| #72 | feat(mcp): MCP 経由のセッション作成失敗をエラーログに記録 | `#242` |
| #73 | fix(tui): 監視/ワーカースレッド異常の panic をエラーログに記録 | `#243` |

ファイルは `.usagi/issues/` 配下の各 `.md` のフロントマターのみ変更。コード・ドキュメントの変更はなし。

## テスト・確認方法

- pre-push フック(`cargo clippy --all-targets -- -D warnings` / `cargo
test`)が通過。
- コード変更を含まないため挙動への影響なし。

Co-authored-by: test <test@example.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant