@sanohiro/casty 0.5.4 → 0.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -1,47 +1,33 @@
1
1
  # casty
2
2
 
3
- Kitty graphics protocol を使った TTY Web ブラウザ。
3
+ ターミナルで本物の Chrome ブラウザを動かす。
4
4
 
5
5
  **[English](README.md)**
6
6
 
7
- ヘッドレス Chrome のレンダリングを Kitty 対応ターミナルに表示し、ターミナル上で完全な Web ブラウジングを実現します。
7
+ casty は w3m や lynx のようなテキストブラウザではありません。ヘッドレス Chrome を起動し、CDP でレンダリング結果を取得して、Kitty graphics protocol でターミナルに描画します。Chrome のリモートデスクトップがターミナルに収まった感じです。
8
8
 
9
- <video src="https://github.com/user-attachments/assets/330bf0c3-dd08-44a5-b627-b90c200d57fe" autoplay loop muted playsinline></video>
9
+ <video src="https://github.com/user-attachments/assets/552f1972-bb53-481e-9516-c36b7e5085d8" autoplay loop muted playsinline></video>
10
+
11
+ ## 仕組み
10
12
 
11
13
  ```
12
- Chrome (Headless Shell) casty Terminal
13
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
14
- Web レンダリング ───→ 高解像度 ─→│ Kitty graphics
15
- JS 実行 │ │ キャプチャ 画面表示
16
- フルブラウザ ←─── 入力ブリッジ ←─│ マウス/キーボード│
17
- └─────────────────┘ └─────────────────┘ └─────────────────┘
14
+ ターミナル(あなた) casty Chrome(ヘッドレス)
15
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
16
+ Kitty ←── Screencast ←── │ 完全な Web
17
+ graphics │ │ + 高解像度 レンダリング
18
+ 画面表示 キャプチャ │ │ JS, CSS,
19
+ │ │ ──→ │ 入力 │ ──→ │ Canvas, │
20
+ │ マウス/KB │ │ ブリッジ │ │ WebGL │
21
+ └──────────────┘ └──────────────┘ └──────────────┘
18
22
  ```
19
23
 
20
- ## 機能
21
-
22
- - ヘッドレス Chrome によるフル Web レンダリング (生 CDP、Playwright 不使用)
23
- - ボット検出回避のステルスパッチ (Google ログイン可能)
24
- - Kitty graphics protocol による画像表示
25
- - マウス操作 (クリック、スクロール、ドラッグ)
26
- - キーボード入力を Chrome にパススルー
27
- - Vimium 風ヒントモード (Alt+F) でキーボードナビゲーション
28
- - アドレスバー + 検索 (Alt+L)
29
- - ブックマーク (アドレスバーで `/b` 検索)
30
- - テキスト選択コピー / クリップボードペースト
31
- - ターミナルのフォントサイズに基づく自動ズーム
32
- - 動的リサイズ (SIGWINCH)
33
- - キーバインド設定 (`~/.casty/keys.json`)
34
- - 各種設定 (`~/.casty/config.json`)
35
- - ファイルダウンロード (`~/Downloads/` に保存)
36
- - ローディングインジケーター
37
- - プロファイル自動クリーンアップによる高速起動
38
-
39
- ## 必要環境
40
-
41
- - **Kitty graphics protocol** 対応ターミナル
42
- - Node.js >= 18
24
+ レンダリングはすべて Chrome がやります。casty はフレームをターミナルに流して入力を返すだけのブリッジ(約1200行)。Playwright も puppeteer も使わず、WebSocket で生 CDP を叩いています。
43
25
 
44
- 動作確認済み: **bcon**, **Ghostty**, **kitty**
26
+ 本物の Chrome なので JavaScript, CSS, Canvas, WebGL 全部動きます。ステルスパッチで Google ログインも通ります。マウスのクリック、スクロール、ドラッグ、キーボード入力 — 普通のブラウザと同じ操作ができます。
27
+
28
+ ## どういうときに使う?
29
+
30
+ SSH でヘッドレスサーバーに入っていて Web ページを確認したいとき、普通は `curl` か `lynx` か X11 転送しかない。casty ならターミナルを離れずに本物のブラウザが使えます。X11 も VNC も Wayland もいらない。Kitty 対応ターミナルさえあれば OK。
45
31
 
46
32
  ## インストール
47
33
 
@@ -50,128 +36,122 @@ npm install -g @sanohiro/casty
50
36
  casty
51
37
  ```
52
38
 
53
- ソースからインストールする場合:
39
+ ソースから:
54
40
 
55
41
  ```bash
56
42
  git clone https://github.com/sanohiro/casty.git
57
- cd casty
58
- npm install
43
+ cd casty && npm install
59
44
  ./bin/casty
60
45
  ```
61
46
 
62
- 初回起動時に Chrome Headless Shell が `~/.casty/browsers/` に自動インストールされます。以降の起動時にバックグラウンドで更新チェックし、常に一世代だけ保持します。
47
+ 初回起動時に Chrome Headless Shell が `~/.casty/browsers/` に自動インストールされます。
48
+
49
+ ### 必要環境
50
+
51
+ - **Kitty graphics protocol** 対応ターミナル(動作確認済み: Ghostty, kitty, bcon)
52
+ - Node.js >= 18
53
+ - `unzip`(Chrome 自動インストールに必要)
63
54
 
64
55
  ## 使い方
65
56
 
66
57
  ```bash
67
58
  casty https://google.com
68
59
  casty https://youtube.com
69
- casty # ホームページを開く (デフォルト: casty GitHub ページ)
60
+ casty # ホームページを開く
70
61
  ```
71
62
 
72
63
  ### キーバインド
73
64
 
74
65
  | キー | アクション |
75
66
  |------|-----------|
76
- | Alt+L | アドレスバーを開く |
77
- | Alt+F | ヒントモード (Vimium 風リンク/ボタン選択) |
78
- | Alt+Left | 戻る |
79
- | Alt+Right | 進む |
67
+ | Alt+L | アドレスバー |
68
+ | Alt+F | ヒントモード(Vimium 風) |
69
+ | Alt+Left / Right | 戻る / 進む |
80
70
  | Alt+C | 選択テキストをコピー |
81
- | Ctrl+V | クリップボードからペースト |
71
+ | Ctrl+V | ペースト |
82
72
  | Ctrl+Q | 終了 |
83
- | Ctrl+C | 終了 (フォールバック) |
84
-
85
- `~/.casty/keys.json` でカスタマイズ可能 (ファイルは自動生成されません):
86
-
87
- ```json
88
- {
89
- "ctrl+q": "quit",
90
- "alt+left": "back",
91
- "alt+right": "forward",
92
- "alt+l": "url_bar",
93
- "alt+f": "hints",
94
- "alt+c": "copy",
95
- "ctrl+v": "paste"
96
- }
97
- ```
98
73
 
99
- ### アドレスバー
100
-
101
- - **Alt+L** または1行目クリックでフォーカス — URL が全選択状態になる
102
- - **Enter** で移動 (URL) または検索 (Google)
103
- - **`/b クエリ`** でブックマーク検索
104
- - **Escape** でキャンセル
105
- - **Ctrl+A** 全選択、**Ctrl+U** 全消去、**Ctrl+W** 単語削除
74
+ `~/.casty/keys.json` でカスタマイズ可能。
106
75
 
107
76
  ### ヒントモード
108
77
 
109
- **Alt+F** でクリック可能/フォーカス可能な要素にラベルを表示。ラベルの文字を入力するとリンク/ボタンのクリックや入力欄へのフォーカスができます。**Escape** でキャンセル。
78
+ **Alt+F** でクリック可能な要素にラベルを表示。ラベルを入力してクリック。ホームロウキー (`a s d f j k l`) を使用。
110
79
 
111
- ラベルはホームロウキー (`a`, `s`, `d`, `f`, `j`, `k`, `l`) を使用 — 7個以下なら1文字、それ以上は2文字 (最大49個)。
80
+ ### アドレスバー
81
+
82
+ **Alt+L** で開く。URL または検索クエリを入力。`/b クエリ` でブックマーク検索。
112
83
 
113
84
  ### ブックマーク
114
85
 
115
- `~/.casty/bookmarks.json` を手動で作成:
86
+ `~/.casty/bookmarks.json` を作成:
116
87
 
117
88
  ```json
118
89
  {
119
90
  "GitHub": "https://github.com",
120
- "Google": "https://google.com",
121
91
  "YouTube": "https://youtube.com"
122
92
  }
123
93
  ```
124
94
 
125
- アドレスバーで `/b クエリ` と入力して検索 (名前・URL の部分一致、大文字小文字無視)。
126
-
127
95
  ### 設定
128
96
 
129
- `~/.casty/config.json` でカスタマイズ可能 (ファイルは自動生成されません):
97
+ `~/.casty/config.json`:
130
98
 
131
99
  ```json
132
100
  {
133
101
  "homeUrl": "https://github.com/sanohiro/casty",
134
102
  "searchUrl": "https://www.google.com/search?q=",
135
103
  "transport": "auto",
136
- "format": "auto"
104
+ "format": "auto",
105
+ "mouseMode": 1002
137
106
  }
138
107
  ```
139
108
 
140
109
  | キー | 説明 | デフォルト |
141
110
  |------|------|-----------|
142
- | `homeUrl` | URL 未指定時に開くページ | `https://github.com/sanohiro/casty` |
143
- | `searchUrl` | 検索エンジン URL (クエリが末尾に付加される) | `https://www.google.com/search?q=` |
144
- | `transport` | Kitty 画像転送方式: `auto`, `file`, `inline` | `auto` (bcon→file、他→inline) |
145
- | `format` | スクリーンショット形式: `auto`, `png`, `jpeg` | `auto` (file→jpeg、inline→png) |
146
-
147
- ## アーキテクチャ
111
+ | `homeUrl` | スタートページ | `https://github.com/sanohiro/casty` |
112
+ | `searchUrl` | 検索エンジン URL | `https://www.google.com/search?q=` |
113
+ | `transport` | 画像転送方式: `auto`, `file`, `inline` | `auto` (bcon/kitty→file、他→inline) |
114
+ | `format` | キャプチャ形式: `auto`, `png`, `jpeg` | `auto` (file→jpeg adaptive、inline→png) |
115
+ | `mouseMode` | `1002` (ボタンイベント) or `1003` (全イベント) | 自動 (Ghostty→1003、他→1002) |
116
+
117
+ ## 比較
118
+
119
+ | | casty | Browsh | w3m/lynx |
120
+ |---|---|---|---|
121
+ | エンジン | Chrome | Firefox | 独自パーサー |
122
+ | 描画 | ピクセルそのまま | テキスト近似 | テキストのみ |
123
+ | JavaScript | 動く | 動く | 動かない |
124
+ | 表示方式 | Kitty graphics | 文字セル | 文字セル |
125
+ | 依存 | Node.js + Chrome | Go + Firefox | 単体 |
126
+
127
+ <details>
128
+ <summary>技術詳細</summary>
129
+
130
+ 全体で約1200行の JavaScript です。中でやっていること:
131
+
132
+ - chrome-headless-shell を起動して生 CDP WebSocket で通信
133
+ - `Runtime.enable` は絶対に送らない(Google ログインが壊れる。これは苦労して発見した)
134
+ - ステルスパッチは `Page.addScriptToEvaluateOnNewDocument` でページロード前に注入
135
+ - フレーム取得はハイブリッド方式: 低解像度 Screencast で変更を検知して、`Page.captureScreenshot` で DPR 対応の高解像度フレームを取得
136
+ - ファイル転送モードでは JPEG→PNG のアダプティブ切替: スクロール中や動画再生中は高速な JPEG、止まったら鮮明な PNG
137
+ - CSI 14t でターミナルのピクセルサイズを取得して自動ズーム
148
138
 
149
139
  ```
150
- bin/
151
- casty # シェルラッパー (Chrome インストール/更新)
152
- casty.js # エントリポイント (ターミナル検出、ズーム、リサイズ)
153
- lib/
154
- browser.js # CDP ブラウザ制御 (起動、Screencast、キャプチャ)
155
- cdp.js # 軽量 CDP WebSocket クライアント
156
- chrome.js # Chrome バイナリ検出、起動、プロファイルクリーンアップ
157
- kitty.js # Kitty graphics protocol 出力 (file/inline)
158
- input.js # マウス/キーボード処理、アクション
159
- hints.js # Vimium 風ヒントモード
160
- urlbar.js # アドレス/検索バー
161
- bookmarks.js # ブックマーク検索
162
- keys.js # キーバインド設定
163
- config.js # ユーザー設定
140
+ bin/casty シェルラッパー(Chrome インストール/更新)
141
+ bin/casty.js エントリポイント(ターミナル、ズーム、リサイズ)
142
+ lib/browser.js CDP ブラウザ制御、フレームキャプチャ
143
+ lib/cdp.js 軽量 CDP WebSocket クライアント
144
+ lib/chrome.js Chrome 検出、起動、プロファイルクリーンアップ
145
+ lib/kitty.js Kitty graphics protocol(file/inline)
146
+ lib/input.js マウス/キーボード処理
147
+ lib/hints.js Vimium 風ヒントモード
148
+ lib/urlbar.js アドレス/検索バー
149
+ lib/config.js ユーザー設定
150
+ lib/keys.js キーバインド設定
151
+ lib/bookmarks.js ブックマーク検索
164
152
  ```
165
153
 
166
- ## 仕組み
167
-
168
- 1. 生 CDP で Chrome Headless Shell を起動 (Playwright 不使用、`Runtime.enable` も送信しない)
169
- 2. ページロード前にステルスパッチを注入してボット検出を回避
170
- 3. ハイブリッドフレーム取得: 低解像度 Screencast を変更検知トリガーとして使い、`Page.captureScreenshot` で高解像度フレームを取得
171
- 4. Kitty graphics protocol でフレームをターミナルに描画
172
- 5. ターミナル入力 (raw mode) をキャプチャし、CDP 経由で Chrome に送信
173
- 6. CSI 14t でターミナルのピクセルサイズを自動検出し、ズームを計算
174
- 7. 起動時にプロファイルをクリーンアップ (Cookie/LocalStorage を保持、キャッシュ類を削除) して高速起動を維持
154
+ </details>
175
155
 
176
156
  ## ライセンス
177
157
 
package/README.md CHANGED
@@ -1,47 +1,33 @@
1
1
  # casty
2
2
 
3
- A TTY web browser powered by Kitty graphics protocol.
3
+ Run a real Chrome browser inside your terminal.
4
4
 
5
- **[日本語](README.ja.md)**
5
+ **[Japanese](README.ja.md)**
6
6
 
7
- casty renders full web pages in your terminal using Chrome's headless rendering, bridging the gap between a headless browser and your Kitty-compatible terminal.
7
+ casty is not a text-mode browser like w3m or lynx. It launches headless Chrome, grabs the rendered frames over CDP, and draws them in your terminal via Kitty graphics protocol. Think of it as a remote desktop for Chrome that fits in a terminal window.
8
8
 
9
- <video src="https://github.com/user-attachments/assets/330bf0c3-dd08-44a5-b627-b90c200d57fe" autoplay loop muted playsinline></video>
9
+ <video src="https://github.com/user-attachments/assets/552f1972-bb53-481e-9516-c36b7e5085d8" autoplay loop muted playsinline></video>
10
+
11
+ ## How It Works
10
12
 
11
13
  ```
12
- Chrome (Headless Shell) casty Terminal
13
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
14
- Web rendering ───→ │ High-res ─→│ Kitty graphics
15
- JS execution │ │ capture display
16
- Full browser ←─── Input bridge ←─│ Mouse/Keyboard
17
- └─────────────────┘ └─────────────────┘ └─────────────────┘
14
+ Terminal (you) casty Chrome (headless)
15
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
16
+ Kitty │ ←──Screencast ←── │ Full web
17
+ graphics │ │ + hi-res rendering
18
+ display capture JS, CSS,
19
+ │ │ ──→ │ Input │ ──→ │ Canvas, │
20
+ │ Mouse/KB │ │ bridge │ │ WebGL │
21
+ └──────────────┘ └──────────────┘ └──────────────┘
18
22
  ```
19
23
 
20
- ## Features
21
-
22
- - Full web rendering via headless Chrome (raw CDP, no Playwright)
23
- - Stealth patches to avoid bot detection (Google login works)
24
- - Kitty graphics protocol for image display
25
- - Mouse support (click, scroll, drag)
26
- - Keyboard passthrough to Chrome
27
- - Vimium-style hint mode (Alt+F) for keyboard navigation
28
- - Address bar with search (Alt+L)
29
- - Bookmarks (`/b` search in address bar)
30
- - Copy selected text / paste from clipboard
31
- - Auto-zoom based on terminal font size
32
- - Dynamic resize (SIGWINCH)
33
- - Configurable keybindings (`~/.casty/keys.json`)
34
- - Configurable settings (`~/.casty/config.json`)
35
- - File downloads to `~/Downloads/`
36
- - Loading indicator
37
- - Fast startup with automatic profile cleanup
38
-
39
- ## Requirements
40
-
41
- - **Kitty graphics protocol** compatible terminal
42
- - Node.js >= 18
24
+ Chrome does all the rendering. casty is just a bridge (~1200 lines) that streams frames to your terminal and sends input back. No Playwright, no puppeteer — raw CDP over WebSocket.
43
25
 
44
- Tested on: **bcon**, **Ghostty**, **kitty**
26
+ Since it's real Chrome, JavaScript, CSS, Canvas, and WebGL all work. Google login works too (stealth patches bypass bot detection). Mouse clicks, scrolling, dragging, typing — everything you'd expect.
27
+
28
+ ## Why use this?
29
+
30
+ If you're working over SSH on a headless server and need to check a web page, your options are usually `curl`, `lynx`, or forwarding X11. casty gives you an actual browser without leaving the terminal. No X11, no VNC, no Wayland — just a Kitty-compatible terminal.
45
31
 
46
32
  ## Installation
47
33
 
@@ -50,128 +36,122 @@ npm install -g @sanohiro/casty
50
36
  casty
51
37
  ```
52
38
 
53
- Or install from source:
39
+ Or from source:
54
40
 
55
41
  ```bash
56
42
  git clone https://github.com/sanohiro/casty.git
57
- cd casty
58
- npm install
43
+ cd casty && npm install
59
44
  ./bin/casty
60
45
  ```
61
46
 
62
- Chrome Headless Shell is automatically installed to `~/.casty/browsers/` on first run and kept up to date on subsequent launches. Only one version is kept at a time.
47
+ Chrome Headless Shell is auto-installed to `~/.casty/browsers/` on first run.
48
+
49
+ ### Requirements
50
+
51
+ - A terminal with **Kitty graphics protocol** support (tested on Ghostty, kitty, bcon)
52
+ - Node.js >= 18
53
+ - `unzip` (for Chrome auto-install)
63
54
 
64
55
  ## Usage
65
56
 
66
57
  ```bash
67
58
  casty https://google.com
68
59
  casty https://youtube.com
69
- casty # opens home page (default: casty GitHub page)
60
+ casty # opens home page
70
61
  ```
71
62
 
72
63
  ### Keybindings
73
64
 
74
65
  | Key | Action |
75
66
  |-----|--------|
76
- | Alt+L | Open address bar |
77
- | Alt+F | Hint mode (Vimium-style link/button selection) |
78
- | Alt+Left | Back |
79
- | Alt+Right | Forward |
67
+ | Alt+L | Address bar |
68
+ | Alt+F | Hint mode (Vimium-style) |
69
+ | Alt+Left / Right | Back / Forward |
80
70
  | Alt+C | Copy selected text |
81
- | Ctrl+V | Paste from clipboard |
71
+ | Ctrl+V | Paste |
82
72
  | Ctrl+Q | Quit |
83
- | Ctrl+C | Quit (fallback) |
84
-
85
- Customize via `~/.casty/keys.json` (file is not created automatically):
86
-
87
- ```json
88
- {
89
- "ctrl+q": "quit",
90
- "alt+left": "back",
91
- "alt+right": "forward",
92
- "alt+l": "url_bar",
93
- "alt+f": "hints",
94
- "alt+c": "copy",
95
- "ctrl+v": "paste"
96
- }
97
- ```
98
73
 
99
- ### Address Bar
100
-
101
- - **Alt+L** or click row 1 to focus — URL is selected, type to replace
102
- - **Enter** to navigate (URLs) or search (Google)
103
- - **`/b query`** to search bookmarks
104
- - **Escape** to cancel
105
- - **Ctrl+A** select all, **Ctrl+U** clear, **Ctrl+W** delete word
74
+ Customizable via `~/.casty/keys.json`.
106
75
 
107
76
  ### Hint Mode
108
77
 
109
- Press **Alt+F** to show labels on clickable and focusable elements. Type the label characters to click a link/button or focus an input field. Press **Escape** to cancel.
78
+ **Alt+F** shows labels on clickable elements. Type the label to click. Labels use home-row keys (`a s d f j k l`).
110
79
 
111
- Labels use home-row keys (`a`, `s`, `d`, `f`, `j`, `k`, `l`) — single character for ≤7 elements, two characters for more (up to 49).
80
+ ### Address Bar
81
+
82
+ **Alt+L** to open. Type a URL or search query. `/b query` searches bookmarks.
112
83
 
113
84
  ### Bookmarks
114
85
 
115
- Create `~/.casty/bookmarks.json` manually:
86
+ Create `~/.casty/bookmarks.json`:
116
87
 
117
88
  ```json
118
89
  {
119
90
  "GitHub": "https://github.com",
120
- "Google": "https://google.com",
121
91
  "YouTube": "https://youtube.com"
122
92
  }
123
93
  ```
124
94
 
125
- Search from the address bar with `/b query` (matches name or URL, case-insensitive).
126
-
127
95
  ### Configuration
128
96
 
129
- Customize via `~/.casty/config.json` (file is not created automatically):
97
+ `~/.casty/config.json`:
130
98
 
131
99
  ```json
132
100
  {
133
101
  "homeUrl": "https://github.com/sanohiro/casty",
134
102
  "searchUrl": "https://www.google.com/search?q=",
135
103
  "transport": "auto",
136
- "format": "auto"
104
+ "format": "auto",
105
+ "mouseMode": 1002
137
106
  }
138
107
  ```
139
108
 
140
109
  | Key | Description | Default |
141
110
  |-----|-------------|---------|
142
- | `homeUrl` | Page opened when no URL is given | `https://github.com/sanohiro/casty` |
143
- | `searchUrl` | Search engine URL (query appended) | `https://www.google.com/search?q=` |
144
- | `transport` | Kitty image transfer: `auto`, `file`, or `inline` | `auto` (bcon→file, others→inline) |
145
- | `format` | Screenshot format: `auto`, `png`, or `jpeg` | `auto` (file→jpeg, inline→png) |
146
-
147
- ## Architecture
111
+ | `homeUrl` | Start page | `https://github.com/sanohiro/casty` |
112
+ | `searchUrl` | Search engine URL | `https://www.google.com/search?q=` |
113
+ | `transport` | Image transfer: `auto`, `file`, `inline` | `auto` (bcon/kitty→file, others→inline) |
114
+ | `format` | Capture format: `auto`, `png`, `jpeg` | `auto` (file→jpeg adaptive, inline→png) |
115
+ | `mouseMode` | `1002` (button-event) or `1003` (any-event) | Auto (Ghostty→1003, others→1002) |
116
+
117
+ ## Comparison
118
+
119
+ | | casty | Browsh | w3m/lynx |
120
+ |---|---|---|---|
121
+ | Engine | Chrome | Firefox | Custom parser |
122
+ | Rendering | Pixel-perfect | Text approximation | Text only |
123
+ | JavaScript | Yes | Yes | No |
124
+ | Display | Kitty graphics | Character cells | Character cells |
125
+ | Dependencies | Node.js + Chrome | Go + Firefox | Standalone |
126
+
127
+ <details>
128
+ <summary>Technical Details</summary>
129
+
130
+ The whole thing is about 1200 lines of JavaScript. Here's what's going on under the hood:
131
+
132
+ - Launches chrome-headless-shell and talks to it via raw CDP WebSocket
133
+ - `Runtime.enable` is never sent (it breaks Google login — discovered the hard way)
134
+ - Stealth patches are injected via `Page.addScriptToEvaluateOnNewDocument` before any page loads
135
+ - Frame capture is hybrid: low-res Screencast triggers change detection, then `Page.captureScreenshot` grabs hi-res frames with proper DPR
136
+ - File transfer mode uses adaptive JPEG→PNG: fast JPEG during scrolling/video, crisp PNG after things settle
137
+ - Terminal pixel size is detected via CSI 14t for auto-zoom
148
138
 
149
139
  ```
150
- bin/
151
- casty # Shell wrapper (Chrome install/update)
152
- casty.js # Entry point (terminal detection, zoom, resize)
153
- lib/
154
- browser.js # CDP browser control (launch, screencast, capture)
155
- cdp.js # Lightweight CDP WebSocket client
156
- chrome.js # Chrome binary detection, launch, profile cleanup
157
- kitty.js # Kitty graphics protocol output (file/inline)
158
- input.js # Mouse/keyboard handling, actions
159
- hints.js # Vimium-style hint mode
160
- urlbar.js # Address/search bar
161
- bookmarks.js # Bookmark search
162
- keys.js # Configurable keybindings
163
- config.js # User configuration
140
+ bin/casty Shell wrapper (Chrome install/update)
141
+ bin/casty.js Entry point (terminal, zoom, resize)
142
+ lib/browser.js CDP browser control, frame capture
143
+ lib/cdp.js Lightweight CDP WebSocket client
144
+ lib/chrome.js Chrome detection, launch, profile cleanup
145
+ lib/kitty.js Kitty graphics protocol (file/inline)
146
+ lib/input.js Mouse/keyboard handling
147
+ lib/hints.js Vimium-style hint mode
148
+ lib/urlbar.js Address/search bar
149
+ lib/config.js User configuration
150
+ lib/keys.js Keybinding config
151
+ lib/bookmarks.js Bookmark search
164
152
  ```
165
153
 
166
- ## How It Works
167
-
168
- 1. Launches Chrome Headless Shell via raw CDP (no Playwright, no `Runtime.enable`)
169
- 2. Injects stealth patches before page load to avoid bot detection
170
- 3. Uses hybrid frame capture: low-res Screencast as change detection trigger, `Page.captureScreenshot` for high-res frames
171
- 4. Renders frames to terminal via Kitty graphics protocol
172
- 5. Captures terminal input (raw mode) and dispatches to Chrome via CDP
173
- 6. Auto-detects terminal pixel size (CSI 14t) for zoom calculation
174
- 7. Cleans up profile on startup (keeps cookies/storage, removes caches) for fast launch
154
+ </details>
175
155
 
176
156
  ## License
177
157
 
package/bin/casty CHANGED
@@ -163,6 +163,12 @@ has_system_chrome() {
163
163
  }
164
164
 
165
165
  if ! has_chrome; then
166
+ # unzip が必要 (Chrome for Testing は zip 配布)
167
+ if ! is_arm64_linux && ! command -v unzip >/dev/null 2>&1; then
168
+ echo "casty: 'unzip' is required to install Chrome Headless Shell." >&2
169
+ echo "casty: Install with: sudo apt install unzip" >&2
170
+ exit 1
171
+ fi
166
172
  if is_arm64_linux; then
167
173
  # ARM64 Linux: Chrome for Testing にバイナリがないので Playwright 経由でインストール
168
174
  install_chromium_playwright
package/bin/casty.js CHANGED
@@ -149,10 +149,11 @@ async function main() {
149
149
 
150
150
  // Frame callback for screencast / captureScreenshot
151
151
  // sendFrame includes cursor positioning (single write)
152
+ let urlBar = null;
152
153
  function onFrame(data) {
153
154
  if (renderPaused) return;
154
155
  sendFrame(data);
155
- urlBar.renderIfDirty();
156
+ if (urlBar) urlBar.renderIfDirty();
156
157
  }
157
158
 
158
159
  // Phase 3: Start screencast
@@ -163,7 +164,7 @@ async function main() {
163
164
  onFrame,
164
165
  });
165
166
 
166
- const urlBar = startInputHandling(client, cssCellW, cssCellH, bindings, pauseRender, forceCapture);
167
+ urlBar = startInputHandling(client, cssCellW, cssCellH, bindings, pauseRender, forceCapture);
167
168
  urlBar.render();
168
169
 
169
170
  // Force capture on page load events (debounced — multiple events fire close together)
package/lib/browser.js CHANGED
@@ -11,10 +11,6 @@ const CHROME_VERSION = '145.0.7632.6';
11
11
  const USER_AGENT = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${CHROME_VERSION} Safari/537.36`;
12
12
  const PROFILE_DIR = join(homedir(), '.casty', 'profile');
13
13
 
14
- // WEBGL_debug_renderer_info extension constants
15
- const GL_UNMASKED_VENDOR = 0x9245; // UNMASKED_VENDOR_WEBGL
16
- const GL_UNMASKED_RENDERER = 0x9246; // UNMASKED_RENDERER_WEBGL
17
-
18
14
  // Screencast settings (low-res stream for change detection)
19
15
  const SCREENCAST_SCALE = 4; // Downscale factor (1/4 resolution)
20
16
  const SCREENCAST_NTH_FRAME = 1; // Every frame (no decimation, faster change detection)
@@ -27,7 +23,6 @@ const CAPTURE_STUCK_RESET = 5000; // Reset stuck capturing flag (ms)
27
23
  // lang: primary language (e.g. "ja", "en-US")
28
24
  function buildStealthScript(lang) {
29
25
  // Build Accept-Language style list: primary, then en-US/en fallbacks
30
- const langBase = lang.split('-')[0]; // "ja", "en", "zh", etc.
31
26
  const languages = [lang];
32
27
  if (lang !== 'en-US' && lang !== 'en') {
33
28
  languages.push('en-US', 'en');
@@ -118,10 +113,11 @@ function buildAcceptLanguage(lang) {
118
113
 
119
114
  // HTTP GET → JSON (using global fetch)
120
115
  async function fetchJson(url) {
121
- const res = await fetch(url);
122
- if (!res.ok) return null;
123
- try { return await res.json(); }
124
- catch { return null; }
116
+ try {
117
+ const res = await fetch(url);
118
+ if (!res.ok) return null;
119
+ return await res.json();
120
+ } catch { return null; }
125
121
  }
126
122
 
127
123
  // Phase 1: Launch Chrome only (no terminal info needed, for parallel execution)
package/lib/hints.js CHANGED
@@ -18,9 +18,13 @@ function makeCollectAndOverlayScript(hintChars, maxHints) {
18
18
  const elems = [];
19
19
  for (const el of all) {
20
20
  if (elems.length >= ${maxHints}) break;
21
- if (el.offsetParent === null && getComputedStyle(el).position !== 'fixed') continue;
22
- const rect = el.getBoundingClientRect();
23
- if (rect.width === 0 || rect.height === 0) continue;
21
+ let rect = el.getBoundingClientRect();
22
+ // Handle display:contents — element has no box, use first child rect
23
+ if (rect.width === 0 || rect.height === 0) {
24
+ const child = el.firstElementChild;
25
+ if (child) rect = child.getBoundingClientRect();
26
+ if (rect.width === 0 || rect.height === 0) continue;
27
+ }
24
28
  if (rect.bottom < 0 || rect.top > window.innerHeight) continue;
25
29
  if (rect.right < 0 || rect.left > window.innerWidth) continue;
26
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanohiro/casty",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
4
4
  "description": "TTY web browser using raw CDP and Kitty graphics protocol",
5
5
  "main": "bin/casty.js",
6
6
  "bin": {