Conversation
Export queryCellSize as QueryCellSize so it can be called from outside the tui package. Add QueryTerminalSize to expose terminal column/row counts via TIOCGWINSZ for use by the new static display mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a --static flag that displays the image to the terminal and exits immediately without entering the interactive TUI. Useful for scripts, pipelines, and quick image previews. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
ターミナルに画像を1回表示して即終了する非インタラクティブモード(--static)を追加し、あわせて TUI 側の端末サイズ取得ユーティリティを再利用可能にする PR です。
Changes:
gaze --static <image>で Bubbletea を起動せずに Kitty Graphics Protocol で表示して終了する経路を追加- 端末セルサイズ取得を
QueryCellSizeとして export し、端末の cols/rows を返すQueryTerminalSizeを追加 - 既存 TUI 実装を export 後の関数名に追従
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/gaze/main.go | --static フラグ追加と runStatic() 実装(画像ロード→端末サイズ取得→表示→終了) |
| internal/adapter/tui/cellsize.go | QueryCellSize export、QueryTerminalSize 追加(非 Windows) |
| internal/adapter/tui/cellsize_windows.go | Windows 向けスタブ(QueryCellSize/QueryTerminalSize) |
| internal/adapter/tui/model.go | QueryCellSize に呼び出し変更 |
| internal/adapter/tui/update.go | QueryCellSize に呼び出し変更 |
cmd/gaze/main.go
Outdated
| // Query terminal dimensions | ||
| cols, rows := tui.QueryTerminalSize() | ||
| if cols <= 0 || rows <= 0 { | ||
| return fmt.Errorf("unable to determine terminal size") |
There was a problem hiding this comment.
QueryTerminalSize() が失敗した場合のエラーメッセージが情報不足です。現状は unable to determine terminal size 固定で、--static が TTY を必要とすることや Windows では常に (0,0) になることが分かりません。失敗理由(TTY でない/未対応 OS など)が伝わる文言にするか、QueryTerminalSize 側で error を返して呼び出し元で %w できる形にすると原因特定が容易になります。
| return fmt.Errorf("unable to determine terminal size") | |
| return fmt.Errorf("unable to determine terminal size: --static mode requires a TTY; on some platforms (e.g., Windows) terminal size may be reported as 0x0") |
cmd/gaze/main.go
Outdated
| vp := &domain.Viewport{ | ||
| TermWidth: cols, | ||
| TermHeight: rows, | ||
| ImgWidth: img.Width, | ||
| ImgHeight: img.Height, | ||
| CellAspectRatio: cellH / cellW, | ||
| ZoomLevel: 1.0, | ||
| } |
There was a problem hiding this comment.
domain.Viewport を struct literal で生成すると、NewViewport が設定する minZoom/maxZoom/zoomStep/panStep が 0 のままになります。現状の --static では主に FitToWindow() のみなので顕在化しにくいですが、将来的に static 表示でもズーム/パン等を追加した場合に Zoom() が常に 0 にクランプされるなど破綻し得ます。domain.NewViewport(...) を使って生成し、必要なフィールドは setter(SetTerminalSize/SetImageSize/SetCellAspectRatio)で埋める形に寄せるのが安全です。
| vp := &domain.Viewport{ | |
| TermWidth: cols, | |
| TermHeight: rows, | |
| ImgWidth: img.Width, | |
| ImgHeight: img.Height, | |
| CellAspectRatio: cellH / cellW, | |
| ZoomLevel: 1.0, | |
| } | |
| vp := domain.NewViewport() | |
| vp.SetTerminalSize(cols, rows) | |
| vp.SetImageSize(img.Width, img.Height) | |
| vp.SetCellAspectRatio(cellH / cellW) |
There was a problem hiding this comment.
e825ed7 で修正 — NewViewport(DefaultConfig().Viewport) + setter に変更しました
| return fmt.Errorf("displaying image: %w", err) | ||
| } | ||
|
|
||
| fmt.Print(output) |
There was a problem hiding this comment.
kittyRenderer.Display(vp) の出力は先頭に \x1b[H(カーソルを左上へ移動)を含むため、--static で alt-screen を使わずに即終了すると、シェルのプロンプトが左上に出て画像を上書きしやすいです。static モードでは表示前後でカーソル位置を save/restore する、表示後に最終行へカーソル移動して改行を出す、または static 用にカーソル移動を含まない表示シーケンスを生成する等の対応を検討してください。
| fmt.Print(output) | |
| // 画像表示後にカーソルをターミナル最終行の先頭へ移動し、改行を出力することで | |
| // シェルのプロンプトが画像の左上を上書きしにくくする。 | |
| cursorToLastLine := fmt.Sprintf("\x1b[%d;1H\n", rows) | |
| fmt.Print(output + cursorToLastLine) |
There was a problem hiding this comment.
e825ed7 で修正 — \x1b[H を除去してインライン表示にし、表示後にカーソルを画像下端に移動するようにしました
| ws, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ) | ||
| if err != nil { | ||
| return 0, 0 | ||
| } |
There was a problem hiding this comment.
QueryTerminalSize のコメントは「判定できない場合は (0, 0)」となっていますが、実装は ws.Col / ws.Row の片方だけが 0 の場合に (0, N) や (N, 0) を返し得ます(QueryCellSize では 0 チェックあり)。呼び出し側が片方だけを見落とすと不正なサイズを使ってしまうので、ws.Col == 0 || ws.Row == 0 の場合は (0,0) に正規化するか、コメントを実装に合わせてください。
| } | |
| } | |
| if ws.Col == 0 || ws.Row == 0 { | |
| return 0, 0 | |
| } |
There was a problem hiding this comment.
2cf8694 で修正 — QueryCellSize と同様の ws.Col == 0 || ws.Row == 0 ガードを追加しました
Calculate display dimensions from the image's native pixel size and cell pixel size so the image is shown at 1:1 resolution. When the native width exceeds the terminal width, scale down proportionally to fit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Normalize to (0, 0) when either ws.Col or ws.Row is zero, matching the existing guard in QueryCellSize. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use NewViewport with DefaultConfig to inherit zoom/pan limits - Improve error message to indicate TTY requirement - Strip cursor-to-home escape so image displays inline at prompt - Move cursor below image after display for clean shell prompt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cmd/gaze/main.go
Outdated
| cols, _ := tui.QueryTerminalSize() | ||
| if cols <= 0 { | ||
| return fmt.Errorf("determining terminal size: --static requires a TTY") | ||
| } |
There was a problem hiding this comment.
Windows では internal/adapter/tui/cellsize_windows.go の QueryTerminalSize() が常に (0,0) を返すため、cmd/gaze/main.go の --static は TTY 上でも必ず失敗します。その際のエラーメッセージが「TTY が必要」に固定されていると原因(未実装/未対応 OS)が誤解されやすいので、Windows では未対応である旨を明示するか、QueryTerminalSize を (cols, rows int, err error) にして未対応を error で返す形にすると切り分けしやすいです。
There was a problem hiding this comment.
gaze は Kitty Graphics Protocol 前提のため現時点で Windows は対象外です。将来的に Windows 対応する際に error 返しへの変更を検討します
| cfg := domain.DefaultConfig() | ||
| vp := domain.NewViewport(cfg.Viewport) | ||
| vp.SetCellAspectRatio(cellH / cellW) | ||
| vp.SetTerminalSize(displayCols, nativeRows) | ||
| vp.SetImageSize(img.Width, img.Height) |
There was a problem hiding this comment.
vp.SetTerminalSize(displayCols, nativeRows) に nativeRows(画像サイズ由来)をそのまま渡すと、縦長画像で nativeRows が非常に大きくなり、kitty への r= 指定やカーソル移動量が実際のターミナル高さと大きく乖離します。QueryTerminalSize() で rows も取れているので、高さ方向も min(nativeRows, rows) のように上限を設けて実ターミナルサイズに合わせるのが安全です。
There was a problem hiding this comment.
0db8f37 で修正 — displayRows = min(nativeRows, rows) でターミナル高さを上限にしました
| // QueryTerminalSize returns (0, 0) on Windows as TIOCGWINSZ is not available. | ||
| func QueryTerminalSize() (cols, rows int) { | ||
| return 0, 0 |
There was a problem hiding this comment.
QueryTerminalSize() が Windows で常に (0,0) を返す実装だと、--static は実質的に Windows では利用不能になります。未対応方針であれば呼び出し側が判別できるように error を返す設計にするか、対応するなら golang.org/x/term.GetSize 等で cols/rows を取得する実装を検討してください。
There was a problem hiding this comment.
gaze は Kitty Graphics Protocol 前提のため現時点で Windows は対象外です
Limit nativeRows to the actual terminal height so very tall images are scaled to fit on screen instead of being clipped by the terminal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| func QueryTerminalSize() (cols, rows int) { | ||
| ws, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ) | ||
| if err != nil { | ||
| return 0, 0 | ||
| } | ||
| if ws.Col == 0 || ws.Row == 0 { | ||
| return 0, 0 | ||
| } | ||
| return int(ws.Col), int(ws.Row) |
There was a problem hiding this comment.
QueryTerminalSize が os.Stdout の FD を前提にしているため、stdout がリダイレクト/パイプの場合にサイズ取得が必ず失敗します(ただし --static の用途としては gaze --static img | cat のようなパイプライン表示も考えられ、その場合でも制御 TTY からサイズを取れた方が扱いやすいです)。os.Stdin.Fd() や /dev/tty を開いて ioctl する、または golang.org/x/term.GetSize を使って「実際に表示される TTY」から cols/rows を取得する実装にすると意図に合います。
There was a problem hiding this comment.
妥当な改善点です。ただし --static で stdout をパイプする場合はエスケープシーケンス自体が無意味になるため、現時点では実用上の問題になりません。将来的に /dev/tty からの取得に変更することを検討します
| imgAspect := float64(img.Width) / float64(img.Height) | ||
| termAspect := float64(displayCols) / (float64(displayRows) * cellAspect) | ||
| dispRows := displayRows | ||
| if imgAspect > termAspect { | ||
| dispRows = int(math.Round(float64(displayCols) / imgAspect / cellAspect)) |
There was a problem hiding this comment.
dispRows の算出ロジックが KittyRenderer.Display() 内の表示サイズ計算(aspect ratio ベース)と重複しています。将来 Display() 側の計算式や丸めが変わると、--static のカーソル移動量だけがズレてプロンプト位置が崩れる可能性があるため、表示に使う displayRows を単一の関数(例: renderer 側で「表示 cols/rows を返す」API)から取得する形に寄せるのが安全です。
There was a problem hiding this comment.
重複は認識しています。表示サイズを返す API を追加すると RendererPort インターフェースの変更が必要になるため、この PR のスコープ外とし将来のリファクタリング候補とします
概要
画像をターミナルに1回表示して即終了する
--staticフラグを追加。パイプラインやスクリプトでの利用、さっと画像を確認したいときに便利。変更内容
cmd/gaze/main.go:--staticフラグの追加とrunStatic()関数の実装。Bubbletea TUI を起動せず、Kitty Graphics Protocol で画像を表示して終了するinternal/adapter/tui/cellsize.go:queryCellSizeをQueryCellSizeに export。QueryTerminalSize関数を追加(cols/rows 取得用)internal/adapter/tui/cellsize_windows.go: 上記の Windows スタブinternal/adapter/tui/model.go,update.go: export 後の関数名に追従テスト計画
make ci)gaze --static <image>で画像が表示され、プロセスが即終了することgaze <image>で従来通りインタラクティブモードが起動すること備考
QueryTerminalSizeは今後他の機能(tmux モード等)でも再利用可能🤖 Generated with Claude Code