packages feed

hpdft-0.4.6.0: dev/0.5-text-ux.md

# テキスト抽出 UX 方針(0.4.6 でリリース)

Phase 1–2(全文 geom 逐次化・Layout O(n) 化)完了後に実装する CLI / 出力 UX の設計メモ。

> **最終決定(2026-07-05、0.4.6.0 として実装・リリース)**: 当初 0.5 として計画したが、TUI ビューワーは性能改善の一環として 0.4 系に含めることにした。役割分担は「`hpdft FILE` = 軽量ビューワー(TUI / パイプ時は legacy 逐次 stdout)」「`hpdft text FILE` = tagged→geom の高品質 stdout(0.4.5 までと同じ)」。`--no-tui` は不要になり削除(text は常に stdout、ビューワーは非 TTY で自動フォールバック)。以下の記述のうち「text デフォルトを legacy にする」案は破棄。`-o` ファイル出力(UX-3)は未実装のまま。

## 背景

| 用途 | 求める品質 | 許容レイテンシ |
|------|-----------|---------------|
| ターミナルでざっと読む(`-p` なし) | 低(stream-order で十分) | **即時・逐次** |
| ファイルに保存(`-o`) | 高(tagged → geom) | 数十秒〜数分可 |

現状は `-p` なしでも tagged/geom 全文バッチを走らせるため、`book.pdf`(150 ページ)で 1 分以上無出力になる。legacy は約 10 秒で全文取得できるが、段落・読み順は劣る。

## 出力モード一覧(目標)

```
hpdft FILE                    # TUI プレビュー(legacy 逐次)
hpdft text FILE               # 同上(明示)
hpdft text -o OUT FILE        # 高品質全文をファイルへ(tagged → geom)
hpdft text -p N FILE          # 1 ページ geom(現行どおり stdout)
hpdft text --legacy FILE       # legacy 全文 stdout(スクリプト向け、TUI なし)
hpdft text --geom -o OUT FILE # geom のみファイル(デバッグ用)
```

`-o` 未指定かつ `-p 0`(全文)のときだけ TUI モード。`-p` 指定・`-o` 指定・パイプ先がファイルでない場合の挙動は下記。

## モード A: TUI プレビュー(デフォルト全文)

### パイプライン

- **legacy の `walkdown` をページ単位で逐次実行**(Document 1 回 open、ページツリーを walk しながら chunk 出力)
- geom / tagged は使わない(品質より速度・即応)

### UI 仕様

- ターミナル**下半分**をビューポートとして固定(`tui` / `vty` / `brick` 等を検討;依存追加は cabal flag `tui` 推奨)
- 先頭から順次テキストを流し込み;バッファはビューポート行数 + α のみ保持(全文メモリ非保持)
- キー操作(最小):
  - `Space` / `j`: スクロール down
  - `k`: up
  - `g` / `G`: 先頭 / 末尾(末尾は legacy 全文取得完了後)
  - `q`: 終了
  - `Ctrl-C`: 中断
- **stderr** に進捗: `hpdft: page 12/150...`(オプション `--quiet` で抑制)

### 非 TTY

- stdout がパイプ / リダイレクトの場合は TUI を使わず **legacy 全文を stdout**(現行 `--legacy` 相当)
- `hpdft FILE | head` 等が壊れないようにする

### 実装フェーズ(UX-1)

1. [x] `PDF.Text.pdfToTextStreamDoc` — page ごとに `(pageNum, total, ByteString)` を IO callback
2. [x] `hpdft` — TTY 判定(`System.IO.hIsTerminalDevice stdout`)
3. [x] 依存なしプロトタイプ: 行単位 legacy 逐次 stdout(TUI 前の段階)
4. [x] 自前 ANSI で下半分ペイン(UX-2)
5. ~~cabal flag `tui` で optional dependency~~(2026-07-05 決定: 不要)

## モード B: ファイル出力(`-o PATH`)

### パイプライン

- **tagged → geom フォールバック**(Phase 1 の逐次 geom を使用)
- `-o` 指定時のみ高品質パス;完了まで時間がかかってもよい
- 進捗は stderr(`hpdft: extracting page 40/150...`)

### オプション整理

| オプション | `-o` あり | `-o` なし(TTY) | `-o` なし(pipe) |
|-----------|----------|-----------------|-----------------|
| (なし) | tagged→geom → file | legacy TUI | legacy stdout |
| `--geom` | geom → file | legacy TUI(geom は -o 専用) | legacy stdout |
| `--tagged` | tagged→geom → file | legacy TUI | legacy stdout |
| `--legacy` | legacy → file | legacy TUI | legacy stdout |
| `-p N` | page N geom → file | page N geom stdout | page N geom stdout |

`-p` と `-o` の併用: 1 ページだけ高品質でファイルへ。

### 実装フェーズ(UX-3)

1. `text` サブコマンドに `-o/--output FILE` 追加
2. `-o` 時 `pdfToTextTaggedBSWith` / 逐次 geom をファイルへ
3. 原子性: `OUT.tmp` へ書き込み完了後 `rename`(途中 kill で壊れたファイルを残さない)
4. `-o` と TUI の相互排他を parser レベルで保証

## モード C: 既存互換

- `hpdft text --legacy FILE` — スクリプト用 legacy 全文 stdout(TUI なし、`--no-tui` フラグでも可)
- Golden / CI — 従来どおり geom/tagged を直接テスト(CLI デフォルト変更の回帰)

## アーキテクチャ

```
                    ┌─────────────────┐
                    │   openDocument   │
                    └────────┬────────┘
                             │
          ┌──────────────────┼──────────────────┐
          ▼                  ▼                  ▼
   walkdownStream      pageTextGeom      pdfToTextTagged
   (legacy/TUI)        (-p / page API)   (-o file)
          │                  │                  │
          ▼                  ▼                  ▼
   TUI / stdout         stdout            OUT file
```

## 依存関係

```
Phase 1–2(性能)  →  UX-3(-o 高品質ファイル)
                   →  UX-1(legacy 逐次ストリーム)
UX-1 プロトタイプ  →  UX-2(brick TUI)
```

## ベンチマーク

- `data/sample/book.pdf` — 全文 geom < 90s(Phase 1 合格)
- `scripts/bench_book.sh` — legacy / geom / -p1 計測
- UX 追加後: TUI 初回表示 < 1s(legacy 1 ページ目相当)

## 決定事項(2026-07-05)

- **TUI ライブラリ: 軽量自前 ANSI に決定**。brick/vty は alternate screen(全画面)前提で「下半分固定」と相性が悪く、依存も重い。エスケープシーケンス直書き+stdin raw モード(`hSetBuffering`/`hSetEcho`)で依存追加なし。cabal flag `tui` も不要になった
- **バッファは全行保持に変更**。`G`(末尾ジャンプ)に必要で、150 ページ書籍でも数 MB のため「viewport+α のみ保持」は簡素化して撤回
- **`--no-tui` フラグ追加**。TTY でも plain stdout を強制(Mode C のスクリプト用途)
- **Windows は対象外**(ANSI 前提、Linux/macOS のみ)
- Golden テストはライブラリ API(`pdfToTextTaggedBS` / `pdfToTextBS`)直呼びのため CLI デフォルト変更の影響なし

## 未決事項

- `-o` で tagged と geom の両方を出すか(`OUT.tagged.txt` / `OUT.geom.txt`)— 現案は **tagged→geom の単一結果**のみ;比較用は `--geom` 併用で別名保存

## バージョン

0.4.6.0(リリース済み): UX-1(legacy 逐次ストリーム)+ UX-2(自前 ANSI TUI ビューワー)  
0.5 以降: `-o` ファイル出力統合(UX-3)