@sanohiro/casty 0.5.3 → 0.5.5

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
@@ -6,7 +6,7 @@ Kitty graphics protocol を使った TTY Web ブラウザ。
6
6
 
7
7
  ヘッドレス Chrome のレンダリングを Kitty 対応ターミナルに表示し、ターミナル上で完全な Web ブラウジングを実現します。
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
10
 
11
11
  ```
12
12
  Chrome (Headless Shell) casty Terminal
@@ -40,6 +40,7 @@ Chrome (Headless Shell) casty Terminal
40
40
 
41
41
  - **Kitty graphics protocol** 対応ターミナル
42
42
  - Node.js >= 18
43
+ - `unzip`(Chrome Headless Shell の自動インストールに必要)
43
44
 
44
45
  動作確認済み: **bcon**, **Ghostty**, **kitty**
45
46
 
@@ -133,7 +134,8 @@ casty # ホームページを開く (デフォルト: casty GitHub ページ)
133
134
  "homeUrl": "https://github.com/sanohiro/casty",
134
135
  "searchUrl": "https://www.google.com/search?q=",
135
136
  "transport": "auto",
136
- "format": "auto"
137
+ "format": "auto",
138
+ "mouseMode": 1002
137
139
  }
138
140
  ```
139
141
 
@@ -141,8 +143,9 @@ casty # ホームページを開く (デフォルト: casty GitHub ページ)
141
143
  |------|------|-----------|
142
144
  | `homeUrl` | URL 未指定時に開くページ | `https://github.com/sanohiro/casty` |
143
145
  | `searchUrl` | 検索エンジン URL (クエリが末尾に付加される) | `https://www.google.com/search?q=` |
144
- | `transport` | Kitty 画像転送方式: `auto`, `file`, `inline` | `auto` (bcon→file、他→inline) |
146
+ | `transport` | Kitty 画像転送方式: `auto`, `file`, `inline` | `auto` (bcon/kitty→file、他→inline) |
145
147
  | `format` | スクリーンショット形式: `auto`, `png`, `jpeg` | `auto` (file→jpeg、inline→png) |
148
+ | `mouseMode` | マウストラッキングモード: `1002` (ボタンイベント) or `1003` (全イベント) | 自動 (Ghostty→1003、他→1002) |
146
149
 
147
150
  ## アーキテクチャ
148
151
 
package/README.md CHANGED
@@ -6,7 +6,7 @@ A TTY web browser powered by Kitty graphics protocol.
6
6
 
7
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.
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
10
 
11
11
  ```
12
12
  Chrome (Headless Shell) casty Terminal
@@ -40,6 +40,7 @@ Chrome (Headless Shell) casty Terminal
40
40
 
41
41
  - **Kitty graphics protocol** compatible terminal
42
42
  - Node.js >= 18
43
+ - `unzip` (for auto-installing Chrome Headless Shell)
43
44
 
44
45
  Tested on: **bcon**, **Ghostty**, **kitty**
45
46
 
@@ -133,7 +134,8 @@ Customize via `~/.casty/config.json` (file is not created automatically):
133
134
  "homeUrl": "https://github.com/sanohiro/casty",
134
135
  "searchUrl": "https://www.google.com/search?q=",
135
136
  "transport": "auto",
136
- "format": "auto"
137
+ "format": "auto",
138
+ "mouseMode": 1002
137
139
  }
138
140
  ```
139
141
 
@@ -141,8 +143,9 @@ Customize via `~/.casty/config.json` (file is not created automatically):
141
143
  |-----|-------------|---------|
142
144
  | `homeUrl` | Page opened when no URL is given | `https://github.com/sanohiro/casty` |
143
145
  | `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) |
146
+ | `transport` | Kitty image transfer: `auto`, `file`, or `inline` | `auto` (bcon/kitty→file, others→inline) |
145
147
  | `format` | Screenshot format: `auto`, `png`, or `jpeg` | `auto` (file→jpeg, inline→png) |
148
+ | `mouseMode` | Mouse tracking mode: `1002` (button-event) or `1003` (any-event) | Auto (Ghostty→1003, others→1002) |
146
149
 
147
150
  ## Architecture
148
151
 
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
@@ -200,5 +206,6 @@ fi
200
206
  # When called from casty.js, CASTY_ENSURE_CHROME is set — just return
201
207
  # When called directly (./bin/casty), launch the app
202
208
  if [ -z "$CASTY_ENSURE_CHROME" ]; then
209
+ export CASTY_ENSURE_CHROME=1
203
210
  exec node "$CASTY_DIR/bin/casty.js" "$@"
204
211
  fi
package/bin/casty.js CHANGED
@@ -5,16 +5,17 @@ import { execFileSync } from 'node:child_process';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { dirname, join } from 'node:path';
7
7
 
8
- // Ensure Chrome is installed (runs shell script that handles download/update)
9
- const __bin = dirname(fileURLToPath(import.meta.url));
10
- try {
11
- execFileSync('bash', [join(__bin, 'casty')], {
12
- stdio: 'inherit',
13
- env: { ...process.env, CASTY_ENSURE_CHROME: '1' },
14
- });
15
- } catch (err) {
16
- // Shell script prints its own errors — exit if Chrome not available
17
- if (err.status) process.exit(err.status);
8
+ // Ensure Chrome is installed (skip if launched from bin/casty shell script)
9
+ if (!process.env.CASTY_ENSURE_CHROME) {
10
+ const __bin = dirname(fileURLToPath(import.meta.url));
11
+ try {
12
+ execFileSync('bash', [join(__bin, 'casty')], {
13
+ stdio: ['ignore', 'inherit', 'inherit'],
14
+ env: { ...process.env, CASTY_ENSURE_CHROME: '1' },
15
+ });
16
+ } catch (err) {
17
+ if (err.status) process.exit(err.status);
18
+ }
18
19
  }
19
20
 
20
21
  import { startBrowser, setupPage, startScreencast, stopScreencast } from '../lib/browser.js';
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/lib/input.js CHANGED
@@ -4,6 +4,7 @@
4
4
  import { toKeyName } from './keys.js';
5
5
  import { UrlBar } from './urlbar.js';
6
6
  import { HintMode } from './hints.js';
7
+ import { loadConfig } from './config.js';
7
8
 
8
9
  // SGR 1006 mouse button codes
9
10
  const MOUSE_BTN_LEFT = 0;
@@ -19,16 +20,29 @@ const CLIPBOARD_TIMEOUT = 1000; // ms
19
20
  // Status message display duration
20
21
  const STATUS_DISPLAY_MS = 2000;
21
22
 
23
+ // Detect mouse tracking mode:
24
+ // 1002 = button-event tracking (reports motion only while button held)
25
+ // 1003 = any-event tracking (reports all motion — needed for hover on Ghostty)
26
+ // Default: 1002 (compatible). Ghostty auto-upgrades to 1003.
27
+ // Override via config.json: "mouseMode": 1002 or 1003
28
+ function detectMouseMode() {
29
+ const config = loadConfig();
30
+ if (config.mouseMode) return config.mouseMode;
31
+ const termProg = (process.env.TERM_PROGRAM || '').toLowerCase();
32
+ if (termProg === 'ghostty') return 1003;
33
+ return 1002;
34
+ }
35
+
36
+ const MOUSE_MODE = detectMouseMode();
37
+
22
38
  // Enable mouse events (SGR 1006 format)
23
- // Mode 1003: any-event tracking (reports motion even without button press)
24
- // This ensures Chrome always knows cursor position for proper hover/click handling
25
39
  export function enableMouse() {
26
- process.stdout.write('\x1b[?1000;1003;1006h');
40
+ process.stdout.write(`\x1b[?1000;${MOUSE_MODE};1006h`);
27
41
  }
28
42
 
29
43
  // Disable mouse events
30
44
  export function disableMouse() {
31
- process.stdout.write('\x1b[?1000;1003;1006l');
45
+ process.stdout.write(`\x1b[?1000;${MOUSE_MODE};1006l`);
32
46
  }
33
47
 
34
48
  // Convert terminal cell coordinates to pixel coordinates
package/lib/kitty.js CHANGED
@@ -37,10 +37,10 @@ function detectTransport() {
37
37
  if (setting === 'file') return 'file';
38
38
  if (setting === 'inline') return 'inline';
39
39
 
40
- // auto: bcon supports t=f
40
+ // auto: file transfer (t=f) for terminals that support it
41
41
  const termProg = process.env.TERM_PROGRAM || '';
42
42
  if (/bcon/i.test(termProg)) return 'file';
43
-
43
+ if (/kitty/i.test(termProg)) return 'file';
44
44
  return 'inline';
45
45
  }
46
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanohiro/casty",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "TTY web browser using raw CDP and Kitty graphics protocol",
5
5
  "main": "bin/casty.js",
6
6
  "bin": {