@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 +6 -3
- package/README.md +6 -3
- package/bin/casty +7 -0
- package/bin/casty.js +11 -10
- package/lib/hints.js +7 -3
- package/lib/input.js +18 -4
- package/lib/kitty.js +2 -2
- package/package.json +1 -1
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/
|
|
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/
|
|
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 (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
if (rect.width === 0 || rect.height === 0)
|
|
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(
|
|
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(
|
|
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:
|
|
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
|
|