@zenbu-labs/pixel 0.0.0 → 0.0.12
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/dist/bin.d.ts +2 -0
- package/dist/bin.js +166 -0
- package/dist/bootstrap.d.ts +1 -0
- package/dist/bootstrap.js +75 -0
- package/dist/context-menu.d.ts +20 -0
- package/dist/context-menu.js +57 -0
- package/dist/debug.d.ts +1 -0
- package/dist/debug.js +9 -0
- package/dist/devtools-layout.d.ts +21 -0
- package/dist/devtools-layout.js +45 -0
- package/dist/devtools.d.ts +33 -0
- package/dist/devtools.js +125 -0
- package/dist/electron/index.d.ts +3 -0
- package/dist/electron/index.js +4 -0
- package/dist/host/guest.d.ts +5 -0
- package/dist/host/guest.js +178 -0
- package/dist/host/server.d.ts +91 -0
- package/dist/host/server.js +296 -0
- package/dist/host/shell.d.ts +35 -0
- package/dist/host/shell.js +113 -0
- package/dist/host/strip.d.ts +6 -0
- package/dist/host/strip.js +63 -0
- package/dist/icons.d.ts +30 -0
- package/dist/icons.js +58 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +39 -0
- package/dist/instances.d.ts +30 -0
- package/dist/instances.js +177 -0
- package/dist/layout.d.ts +5 -0
- package/dist/layout.js +34 -0
- package/dist/popup.d.ts +21 -0
- package/dist/popup.js +47 -0
- package/dist/preload/index.d.ts +15 -0
- package/dist/preload/index.js +3 -0
- package/dist/react/colors.d.ts +3 -0
- package/dist/react/colors.js +25 -0
- package/dist/react/components.d.ts +21 -0
- package/dist/react/components.js +45 -0
- package/dist/react/devtools/app.d.ts +3 -0
- package/dist/react/devtools/app.js +89 -0
- package/dist/react/devtools/console-capture.d.ts +1 -0
- package/dist/react/devtools/console-capture.js +41 -0
- package/dist/react/devtools/controller.d.ts +45 -0
- package/dist/react/devtools/controller.js +252 -0
- package/dist/react/devtools/elements.d.ts +4 -0
- package/dist/react/devtools/elements.js +152 -0
- package/dist/react/devtools/export-profile.d.ts +2 -0
- package/dist/react/devtools/export-profile.js +225 -0
- package/dist/react/devtools/fiber-hook.d.ts +1 -0
- package/dist/react/devtools/fiber-hook.js +89 -0
- package/dist/react/devtools/log-panel.d.ts +4 -0
- package/dist/react/devtools/log-panel.js +113 -0
- package/dist/react/devtools/profiler.d.ts +11 -0
- package/dist/react/devtools/profiler.js +374 -0
- package/dist/react/devtools/store.d.ts +27 -0
- package/dist/react/devtools/store.js +61 -0
- package/dist/react/devtools/stores.d.ts +92 -0
- package/dist/react/devtools/stores.js +41 -0
- package/dist/react/devtools/theme.d.ts +35 -0
- package/dist/react/devtools/theme.js +77 -0
- package/dist/react/devtools/ui.d.ts +24 -0
- package/dist/react/devtools/ui.js +59 -0
- package/dist/react/index.d.ts +110 -0
- package/dist/react/index.js +538 -0
- package/dist/react/markdown.d.ts +25 -0
- package/dist/react/markdown.js +270 -0
- package/dist/react/native.d.ts +116 -0
- package/dist/react/native.js +56 -0
- package/dist/react/reconciler-config.d.ts +241 -0
- package/dist/react/reconciler-config.js +412 -0
- package/dist/react/styles.d.ts +88 -0
- package/dist/react/styles.js +85 -0
- package/dist/react/surface.d.ts +55 -0
- package/dist/react/surface.js +74 -0
- package/dist/registry.d.ts +53 -0
- package/dist/registry.js +193 -0
- package/dist/root.d.ts +19 -0
- package/dist/root.js +342 -0
- package/dist/sandbox.d.ts +4 -0
- package/dist/sandbox.js +84 -0
- package/dist/scale.d.ts +7 -0
- package/dist/scale.js +31 -0
- package/dist/ssh/index.d.ts +58 -0
- package/dist/ssh/index.js +445 -0
- package/dist/terminal/applescript.d.ts +2 -0
- package/dist/terminal/applescript.js +20 -0
- package/dist/terminal/detect.d.ts +10 -0
- package/dist/terminal/detect.js +34 -0
- package/dist/terminal/graphics.d.ts +9 -0
- package/dist/terminal/graphics.js +142 -0
- package/dist/terminal/index.d.ts +12 -0
- package/dist/terminal/index.js +23 -0
- package/dist/terminal/run.d.ts +2 -0
- package/dist/terminal/run.js +15 -0
- package/dist/terminal/shared.d.ts +20 -0
- package/dist/terminal/shared.js +88 -0
- package/dist/terminal/terminal.d.ts +66 -0
- package/dist/terminal/terminal.js +6 -0
- package/dist/terminal/terminals/cmux.d.ts +2 -0
- package/dist/terminal/terminals/cmux.js +98 -0
- package/dist/terminal/terminals/ghostty-script.d.ts +1 -0
- package/dist/terminal/terminals/ghostty-script.js +179 -0
- package/dist/terminal/terminals/ghostty.d.ts +2 -0
- package/dist/terminal/terminals/ghostty.js +336 -0
- package/dist/terminal/terminals/herdr.d.ts +2 -0
- package/dist/terminal/terminals/herdr.js +141 -0
- package/dist/terminal/terminals/index.d.ts +5 -0
- package/dist/terminal/terminals/index.js +26 -0
- package/dist/terminal/terminals/kitty.d.ts +2 -0
- package/dist/terminal/terminals/kitty.js +209 -0
- package/dist/terminal/terminals/supacode.d.ts +2 -0
- package/dist/terminal/terminals/supacode.js +79 -0
- package/dist/terminal/terminals/tmux.d.ts +2 -0
- package/dist/terminal/terminals/tmux.js +132 -0
- package/dist/terminal/terminals/tty7.d.ts +2 -0
- package/dist/terminal/terminals/tty7.js +58 -0
- package/dist/terminal/terminals/vscode.d.ts +2 -0
- package/dist/terminal/terminals/vscode.js +13 -0
- package/dist/terminal/terminals/wezterm.d.ts +2 -0
- package/dist/terminal/terminals/wezterm.js +59 -0
- package/dist/theme.d.ts +22 -0
- package/dist/theme.js +40 -0
- package/dist/web/browser-session.d.ts +13 -0
- package/dist/web/browser-session.js +186 -0
- package/dist/web/cursor.d.ts +1 -0
- package/dist/web/cursor.js +24 -0
- package/dist/web/devtools.d.ts +39 -0
- package/dist/web/devtools.js +300 -0
- package/dist/web/favicon.d.ts +7 -0
- package/dist/web/favicon.js +73 -0
- package/dist/web/frame-rate.d.ts +1 -0
- package/dist/web/frame-rate.js +12 -0
- package/dist/web/host.d.ts +123 -0
- package/dist/web/host.js +573 -0
- package/dist/web/input.d.ts +49 -0
- package/dist/web/input.js +468 -0
- package/dist/web/offscreen.d.ts +2 -0
- package/dist/web/offscreen.js +23 -0
- package/dist/web/paint.d.ts +11 -0
- package/dist/web/paint.js +128 -0
- package/dist/web/popup.d.ts +40 -0
- package/dist/web/popup.js +144 -0
- package/dist/web/session.d.ts +20 -0
- package/dist/web/session.js +110 -0
- package/dist/web/types.d.ts +38 -0
- package/dist/web/types.js +39 -0
- package/dist/web/zoom.d.ts +5 -0
- package/dist/web/zoom.js +36 -0
- package/dist/webview.d.ts +55 -0
- package/dist/webview.js +481 -0
- package/package.json +69 -3
- package/scripts/apparmor.sh +71 -0
- package/scripts/postinstall.mjs +150 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detect = detect;
|
|
4
|
+
exports.checkTerminal = checkTerminal;
|
|
5
|
+
exports.cannotOpenPanes = cannotOpenPanes;
|
|
6
|
+
const graphics_1 = require("./graphics");
|
|
7
|
+
const run_1 = require("./run");
|
|
8
|
+
const terminals_1 = require("./terminals");
|
|
9
|
+
function detect(env = process.env, run) {
|
|
10
|
+
const runIn = run ?? (0, run_1.shellIn)(env);
|
|
11
|
+
for (const recognise of terminals_1.TERMINALS) {
|
|
12
|
+
const terminal = recognise(env, runIn);
|
|
13
|
+
if (terminal)
|
|
14
|
+
return terminal;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
async function checkTerminal(terminal, env = process.env) {
|
|
19
|
+
await terminal?.prepare?.();
|
|
20
|
+
if (env[graphics_1.SKIP_ENV])
|
|
21
|
+
return { terminal, graphics: "supported" };
|
|
22
|
+
const probed = await (0, graphics_1.probeGraphics)(terminal);
|
|
23
|
+
const graphics = probed === "unknown" ? (terminal ? "supported" : "unsupported") : probed;
|
|
24
|
+
return { terminal, graphics };
|
|
25
|
+
}
|
|
26
|
+
function cannotOpenPanes(terminal) {
|
|
27
|
+
const who = terminal
|
|
28
|
+
? `${terminal.name} cannot open panes from a command`
|
|
29
|
+
: "unrecogonized terminal";
|
|
30
|
+
const recommendation = process.platform === "darwin"
|
|
31
|
+
? "We recommend Ghostty (https://ghostty.org/download)."
|
|
32
|
+
: "We recommend Kitty (https://sw.kovidgoyal.net/kitty/binary)";
|
|
33
|
+
return `${who}. ${recommendation}`;
|
|
34
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Terminal } from "./terminal";
|
|
2
|
+
export type GraphicsSupport = "supported" | "unsupported" | "unknown";
|
|
3
|
+
export declare const SKIP_ENV = "PIXEL_SKIP_GRAPHICS_CHECK";
|
|
4
|
+
export declare function probeGraphics(terminal: Terminal | null): Promise<GraphicsSupport>;
|
|
5
|
+
export declare function panePixels(): Promise<{
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
} | null>;
|
|
9
|
+
export declare function unsupportedGraphicsMessage(color?: boolean): string;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SKIP_ENV = void 0;
|
|
7
|
+
exports.probeGraphics = probeGraphics;
|
|
8
|
+
exports.panePixels = panePixels;
|
|
9
|
+
exports.unsupportedGraphicsMessage = unsupportedGraphicsMessage;
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
exports.SKIP_ENV = "PIXEL_SKIP_GRAPHICS_CHECK";
|
|
12
|
+
const PROBE_ID = 4207;
|
|
13
|
+
const PROBE_TIMEOUT_MS = 500;
|
|
14
|
+
function passthrough(seq) {
|
|
15
|
+
return `\x1bPtmux;${seq.replaceAll("\x1b", "\x1b\x1b")}\x1b\\`;
|
|
16
|
+
}
|
|
17
|
+
function probeSequence(wrap) {
|
|
18
|
+
const query = `\x1b_Gi=${PROBE_ID},a=q,t=d,f=24,s=1,v=1;AAAA\x1b\\`;
|
|
19
|
+
return `${wrap ? passthrough(query) : query}\x1b[c`;
|
|
20
|
+
}
|
|
21
|
+
function graphicsReply(buffer) {
|
|
22
|
+
const needle = `Gi=${PROBE_ID};`;
|
|
23
|
+
const at = buffer.indexOf(needle);
|
|
24
|
+
if (at < 0)
|
|
25
|
+
return null;
|
|
26
|
+
const rest = buffer.slice(at + needle.length);
|
|
27
|
+
if (rest.length < 2)
|
|
28
|
+
return null;
|
|
29
|
+
return rest.startsWith("OK");
|
|
30
|
+
}
|
|
31
|
+
function probeGraphics(terminal) {
|
|
32
|
+
const stdin = process.stdin;
|
|
33
|
+
if (!stdin.isTTY || !process.stdout.isTTY || !stdin.setRawMode) {
|
|
34
|
+
return Promise.resolve("unknown");
|
|
35
|
+
}
|
|
36
|
+
const wasRaw = stdin.isRaw;
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
let buffer = "";
|
|
39
|
+
let timer = null;
|
|
40
|
+
let done = false;
|
|
41
|
+
const finish = (graphics) => {
|
|
42
|
+
if (done)
|
|
43
|
+
return;
|
|
44
|
+
done = true;
|
|
45
|
+
if (timer)
|
|
46
|
+
clearTimeout(timer);
|
|
47
|
+
stdin.off("data", onData);
|
|
48
|
+
try {
|
|
49
|
+
if (!wasRaw)
|
|
50
|
+
stdin.setRawMode?.(false);
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
if (!stdin.isPaused())
|
|
54
|
+
stdin.pause();
|
|
55
|
+
resolve(graphics);
|
|
56
|
+
};
|
|
57
|
+
const onData = (chunk) => {
|
|
58
|
+
buffer += chunk.toString("binary");
|
|
59
|
+
const reply = graphicsReply(buffer);
|
|
60
|
+
if (reply !== null)
|
|
61
|
+
return finish(reply ? "supported" : "unsupported");
|
|
62
|
+
if (buffer.length > 1024)
|
|
63
|
+
finish("unknown");
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
stdin.setRawMode(true);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return finish("unknown");
|
|
70
|
+
}
|
|
71
|
+
stdin.resume();
|
|
72
|
+
stdin.on("data", onData);
|
|
73
|
+
timer = setTimeout(() => finish("unknown"), PROBE_TIMEOUT_MS);
|
|
74
|
+
try {
|
|
75
|
+
node_fs_1.default.writeSync(1, probeSequence(terminal?.wrapper === "tmux"));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
finish("unknown");
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function panePixels() {
|
|
83
|
+
const stdin = process.stdin;
|
|
84
|
+
if (!stdin.isTTY || !process.stdout.isTTY || !stdin.setRawMode)
|
|
85
|
+
return Promise.resolve(null);
|
|
86
|
+
const wasRaw = stdin.isRaw;
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
let buffer = "";
|
|
89
|
+
let timer = null;
|
|
90
|
+
let done = false;
|
|
91
|
+
const finish = (value) => {
|
|
92
|
+
if (done)
|
|
93
|
+
return;
|
|
94
|
+
done = true;
|
|
95
|
+
if (timer)
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
stdin.off("data", onData);
|
|
98
|
+
try {
|
|
99
|
+
if (!wasRaw)
|
|
100
|
+
stdin.setRawMode?.(false);
|
|
101
|
+
}
|
|
102
|
+
catch { }
|
|
103
|
+
if (!stdin.isPaused())
|
|
104
|
+
stdin.pause();
|
|
105
|
+
resolve(value);
|
|
106
|
+
};
|
|
107
|
+
const onData = (chunk) => {
|
|
108
|
+
buffer += chunk.toString("binary");
|
|
109
|
+
const reply = /\x1b\[4;(\d+);(\d+)t/.exec(buffer);
|
|
110
|
+
if (reply)
|
|
111
|
+
finish({ width: Number(reply[2]), height: Number(reply[1]) });
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
stdin.setRawMode(true);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return finish(null);
|
|
118
|
+
}
|
|
119
|
+
stdin.resume();
|
|
120
|
+
stdin.on("data", onData);
|
|
121
|
+
timer = setTimeout(() => finish(null), PROBE_TIMEOUT_MS);
|
|
122
|
+
try {
|
|
123
|
+
node_fs_1.default.writeSync(1, "\x1b[14t");
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
finish(null);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function unsupportedGraphicsMessage(color = false) {
|
|
131
|
+
const sgr = (code, text) => (color ? `\x1b[${code}m${text}\x1b[0m` : text);
|
|
132
|
+
return [
|
|
133
|
+
"",
|
|
134
|
+
` ${sgr("1", "Unsupported terminal: this terminal cannot display graphics")}`,
|
|
135
|
+
"",
|
|
136
|
+
` ${sgr("2", "We recommend Ghostty:")}`,
|
|
137
|
+
` ${sgr("4", "https://ghostty.org/download")}`,
|
|
138
|
+
"",
|
|
139
|
+
` ${sgr("2", "Note: any terminal that supports the kitty graphics protocol is supported")}`,
|
|
140
|
+
"",
|
|
141
|
+
].join("\n");
|
|
142
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/// <reference path="../../electron/electron.d.ts" preserve="true" />
|
|
2
|
+
export type { Detect, Direction, ListPanesOptions, Pane, PaneContext, PaneDetails, SplitRequest, Terminal } from "./terminal";
|
|
3
|
+
export { canSplit } from "./terminal";
|
|
4
|
+
export type { Run } from "./run";
|
|
5
|
+
export { shellIn } from "./run";
|
|
6
|
+
export { bracketedPaste, callerTty, shellLiteral } from "./shared";
|
|
7
|
+
export type { TerminalCheck } from "./detect";
|
|
8
|
+
export { cannotOpenPanes, checkTerminal, detect } from "./detect";
|
|
9
|
+
export type { GraphicsSupport } from "./graphics";
|
|
10
|
+
export type { Instance } from "../instances";
|
|
11
|
+
export { findOwner, listInstances, PROTOCOL as EMBED_PROTOCOL } from "../instances";
|
|
12
|
+
export { probeGraphics, unsupportedGraphicsMessage, SKIP_ENV as GRAPHICS_SKIP_ENV } from "./graphics";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GRAPHICS_SKIP_ENV = exports.unsupportedGraphicsMessage = exports.probeGraphics = exports.EMBED_PROTOCOL = exports.listInstances = exports.findOwner = exports.detect = exports.checkTerminal = exports.cannotOpenPanes = exports.shellLiteral = exports.callerTty = exports.bracketedPaste = exports.shellIn = exports.canSplit = void 0;
|
|
4
|
+
var terminal_1 = require("./terminal");
|
|
5
|
+
Object.defineProperty(exports, "canSplit", { enumerable: true, get: function () { return terminal_1.canSplit; } });
|
|
6
|
+
var run_1 = require("./run");
|
|
7
|
+
Object.defineProperty(exports, "shellIn", { enumerable: true, get: function () { return run_1.shellIn; } });
|
|
8
|
+
var shared_1 = require("./shared");
|
|
9
|
+
Object.defineProperty(exports, "bracketedPaste", { enumerable: true, get: function () { return shared_1.bracketedPaste; } });
|
|
10
|
+
Object.defineProperty(exports, "callerTty", { enumerable: true, get: function () { return shared_1.callerTty; } });
|
|
11
|
+
Object.defineProperty(exports, "shellLiteral", { enumerable: true, get: function () { return shared_1.shellLiteral; } });
|
|
12
|
+
var detect_1 = require("./detect");
|
|
13
|
+
Object.defineProperty(exports, "cannotOpenPanes", { enumerable: true, get: function () { return detect_1.cannotOpenPanes; } });
|
|
14
|
+
Object.defineProperty(exports, "checkTerminal", { enumerable: true, get: function () { return detect_1.checkTerminal; } });
|
|
15
|
+
Object.defineProperty(exports, "detect", { enumerable: true, get: function () { return detect_1.detect; } });
|
|
16
|
+
var instances_1 = require("../instances");
|
|
17
|
+
Object.defineProperty(exports, "findOwner", { enumerable: true, get: function () { return instances_1.findOwner; } });
|
|
18
|
+
Object.defineProperty(exports, "listInstances", { enumerable: true, get: function () { return instances_1.listInstances; } });
|
|
19
|
+
Object.defineProperty(exports, "EMBED_PROTOCOL", { enumerable: true, get: function () { return instances_1.PROTOCOL; } });
|
|
20
|
+
var graphics_1 = require("./graphics");
|
|
21
|
+
Object.defineProperty(exports, "probeGraphics", { enumerable: true, get: function () { return graphics_1.probeGraphics; } });
|
|
22
|
+
Object.defineProperty(exports, "unsupportedGraphicsMessage", { enumerable: true, get: function () { return graphics_1.unsupportedGraphicsMessage; } });
|
|
23
|
+
Object.defineProperty(exports, "GRAPHICS_SKIP_ENV", { enumerable: true, get: function () { return graphics_1.SKIP_ENV; } });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.shellIn = shellIn;
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const exec = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
7
|
+
function shellIn(env) {
|
|
8
|
+
return async (bin, args, input) => {
|
|
9
|
+
const running = exec(bin, args, { env, maxBuffer: 4 * 1024 * 1024, timeout: 8000 });
|
|
10
|
+
if (input !== undefined)
|
|
11
|
+
running.child.stdin?.end(input);
|
|
12
|
+
const { stdout } = await running;
|
|
13
|
+
return stdout;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Pane } from "./terminal";
|
|
2
|
+
export interface CallerTty {
|
|
3
|
+
path: string | null;
|
|
4
|
+
denied: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface PaneRect {
|
|
7
|
+
id: string;
|
|
8
|
+
left: number;
|
|
9
|
+
top: number;
|
|
10
|
+
right: number;
|
|
11
|
+
bottom: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function adjacentPane(from: PaneRect, candidates: PaneRect[], direction: "right" | "left" | "down" | "up", gap?: number): PaneRect | null;
|
|
14
|
+
export declare function callerTty(): CallerTty;
|
|
15
|
+
export declare function setPaneWorkingDirectory(tty: string, directory: string): void;
|
|
16
|
+
export declare function paneById(panes: () => Promise<Pane[]>, id: string | null | undefined): Promise<Pane | null>;
|
|
17
|
+
export declare function shellQuote(argv: string[]): string;
|
|
18
|
+
export declare const sleep: (ms: number) => Promise<unknown>;
|
|
19
|
+
export declare function shellLiteral(text: string): string;
|
|
20
|
+
export declare function bracketedPaste(text: string): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sleep = void 0;
|
|
7
|
+
exports.adjacentPane = adjacentPane;
|
|
8
|
+
exports.callerTty = callerTty;
|
|
9
|
+
exports.setPaneWorkingDirectory = setPaneWorkingDirectory;
|
|
10
|
+
exports.paneById = paneById;
|
|
11
|
+
exports.shellQuote = shellQuote;
|
|
12
|
+
exports.shellLiteral = shellLiteral;
|
|
13
|
+
exports.bracketedPaste = bracketedPaste;
|
|
14
|
+
const node_child_process_1 = require("node:child_process");
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
17
|
+
function adjacentPane(from, candidates, direction, gap = 2) {
|
|
18
|
+
const touches = (candidate) => {
|
|
19
|
+
switch (direction) {
|
|
20
|
+
case "right":
|
|
21
|
+
return candidate.left > from.right && candidate.left <= from.right + gap;
|
|
22
|
+
case "left":
|
|
23
|
+
return candidate.right < from.left && candidate.right >= from.left - gap;
|
|
24
|
+
case "down":
|
|
25
|
+
return candidate.top > from.bottom && candidate.top <= from.bottom + gap;
|
|
26
|
+
case "up":
|
|
27
|
+
return candidate.bottom < from.top && candidate.bottom >= from.top - gap;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const overlap = (candidate) => direction === "right" || direction === "left"
|
|
31
|
+
? Math.min(candidate.bottom, from.bottom) - Math.max(candidate.top, from.top)
|
|
32
|
+
: Math.min(candidate.right, from.right) - Math.max(candidate.left, from.left);
|
|
33
|
+
let best = null;
|
|
34
|
+
for (const candidate of candidates) {
|
|
35
|
+
if (candidate.id === from.id || !touches(candidate) || overlap(candidate) < 0)
|
|
36
|
+
continue;
|
|
37
|
+
if (!best || overlap(candidate) > overlap(best))
|
|
38
|
+
best = candidate;
|
|
39
|
+
}
|
|
40
|
+
return best;
|
|
41
|
+
}
|
|
42
|
+
function callerTty() {
|
|
43
|
+
let pid = process.pid;
|
|
44
|
+
for (let hops = 0; hops < 30 && pid > 1; hops++) {
|
|
45
|
+
let out;
|
|
46
|
+
try {
|
|
47
|
+
out = (0, node_child_process_1.execFileSync)("ps", ["-o", "ppid=,tty=", "-p", String(pid)], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
}).trim();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return { path: null, denied: hops === 0 };
|
|
53
|
+
}
|
|
54
|
+
if (!out)
|
|
55
|
+
return { path: null, denied: hops === 0 };
|
|
56
|
+
const [ppid, tty] = out.split(/\s+/);
|
|
57
|
+
if (tty && tty !== "??" && tty !== "?")
|
|
58
|
+
return { path: `/dev/${tty}`, denied: false };
|
|
59
|
+
pid = Number(ppid);
|
|
60
|
+
if (!Number.isFinite(pid))
|
|
61
|
+
return { path: null, denied: false };
|
|
62
|
+
}
|
|
63
|
+
return { path: null, denied: false };
|
|
64
|
+
}
|
|
65
|
+
function setPaneWorkingDirectory(tty, directory) {
|
|
66
|
+
const encoded = directory.split("/").map(encodeURIComponent).join("/");
|
|
67
|
+
node_fs_1.default.writeFileSync(tty, `\x1b]7;file://${node_os_1.default.hostname()}${encoded}\x07`);
|
|
68
|
+
}
|
|
69
|
+
async function paneById(panes, id) {
|
|
70
|
+
if (!id)
|
|
71
|
+
return null;
|
|
72
|
+
return (await panes()).find((pane) => pane.id === id) ?? null;
|
|
73
|
+
}
|
|
74
|
+
function shellQuote(argv) {
|
|
75
|
+
return argv
|
|
76
|
+
.map((arg) => arg !== "" && /^[\w\-./:=+@%,]+$/.test(arg) ? arg : `'${arg.replaceAll("'", `'\\''`)}'`)
|
|
77
|
+
.join(" ");
|
|
78
|
+
}
|
|
79
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
80
|
+
exports.sleep = sleep;
|
|
81
|
+
function shellLiteral(text) {
|
|
82
|
+
return `'${text.replace(/['\\]/g, (char) => (char === "'" ? "'\\''" : "'\\\\'"))}'`;
|
|
83
|
+
}
|
|
84
|
+
function bracketedPaste(text) {
|
|
85
|
+
if (!text.includes("\n"))
|
|
86
|
+
return text;
|
|
87
|
+
return `\x1b[200~${text.replaceAll("\x1b[201~", "")}\x1b[201~`;
|
|
88
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Run } from "./run";
|
|
2
|
+
export type Direction = "right" | "left" | "down" | "up";
|
|
3
|
+
export interface Pane {
|
|
4
|
+
id: string;
|
|
5
|
+
tab: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PaneDetails extends Pane {
|
|
8
|
+
tty: string | null;
|
|
9
|
+
command: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface PaneContext {
|
|
12
|
+
tty: string | null;
|
|
13
|
+
cwd: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ListPanesOptions {
|
|
16
|
+
commands?: (command: string) => boolean;
|
|
17
|
+
tty?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface SplitRequest {
|
|
20
|
+
from: Pane;
|
|
21
|
+
direction: Direction;
|
|
22
|
+
command: string[];
|
|
23
|
+
size: number | null;
|
|
24
|
+
tty: string | null;
|
|
25
|
+
}
|
|
26
|
+
export interface Terminal {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/**
|
|
29
|
+
* When terminal-browser tries to write to the TTY to render graphics using the
|
|
30
|
+
* kitty graphics protocol, tmux will by default not forward those escape
|
|
31
|
+
* codes to the outer TTY. But, tmux supports a passthrough mode if the content
|
|
32
|
+
* is wrapped in tmux specific escape codes. We implement this "wrapping" functionality
|
|
33
|
+
* internally, and allow a backend to configure what kind of wrapping should be applied (if any)
|
|
34
|
+
*/
|
|
35
|
+
readonly wrapper?: "tmux";
|
|
36
|
+
/**
|
|
37
|
+
* When terminal-browser is rendered, it is scaled by the DPI of the display
|
|
38
|
+
* Terminals that report css based pixels when queried already perform that normalization, so we need an explicit flag to avoid rescaling
|
|
39
|
+
*/
|
|
40
|
+
readonly reportsCssPixels?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Allows code to be ran at startup, useful for preparing the terminal environment for terminal-browser
|
|
43
|
+
*/
|
|
44
|
+
prepare?(): void | Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Allows terminal-browser to know which pane its being called from inside the terminal.
|
|
47
|
+
* This gives terminal-browser the ability to know if a terminal-browser instance
|
|
48
|
+
* is open inside the terminal-pane, and powers features such as:
|
|
49
|
+
* - terminal-browser ls lists the browers inside the current terminal tab
|
|
50
|
+
* - terminal-browser new-tab [url] will open in the browser inside the current terminal tab by default
|
|
51
|
+
*/
|
|
52
|
+
getCurrentPane?(context: PaneContext): Promise<Pane | null>;
|
|
53
|
+
/**
|
|
54
|
+
* This powers terminal-browser's --split argument. This is useful when using terminal-browser w/ coding agents
|
|
55
|
+
* since it allows agents to open a browser next to their TUI without having to know how to
|
|
56
|
+
* script the current terminal they are running in
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
split?(request: SplitRequest): Promise<void>;
|
|
60
|
+
listPanes?(options?: ListPanesOptions): Promise<PaneDetails[]>;
|
|
61
|
+
neighbor?(from: Pane, direction: Direction): Promise<Pane | null>;
|
|
62
|
+
sendText?(pane: string, text: string): Promise<void>;
|
|
63
|
+
focusPane?(pane: string): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export type Detect = (env: NodeJS.ProcessEnv, run: Run) => Terminal | null;
|
|
66
|
+
export declare function canSplit(terminal: Terminal | null): boolean;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cmux = void 0;
|
|
4
|
+
const shared_1 = require("../shared");
|
|
5
|
+
const cmux = (env, run) => {
|
|
6
|
+
if (!env.CMUX_SURFACE_ID)
|
|
7
|
+
return null;
|
|
8
|
+
const binary = env.CMUX_BUNDLED_CLI_PATH ?? "cmux";
|
|
9
|
+
const cmux = (args) => run(binary, args);
|
|
10
|
+
const asked = async (args) => JSON.parse(await cmux([...args, "--json", "--id-format", "both"]));
|
|
11
|
+
async function panes() {
|
|
12
|
+
const listed = (await asked(["list-panes"])).panes ?? [];
|
|
13
|
+
const found = [];
|
|
14
|
+
for (const pane of listed) {
|
|
15
|
+
const paneId = pane.id ?? pane.ref;
|
|
16
|
+
if (!paneId)
|
|
17
|
+
continue;
|
|
18
|
+
const surfaces = (await asked(["list-pane-surfaces", "--pane", paneId]))
|
|
19
|
+
.surfaces ?? [];
|
|
20
|
+
for (const surface of surfaces) {
|
|
21
|
+
if (!surface.id)
|
|
22
|
+
continue;
|
|
23
|
+
found.push({ id: surface.id, tab: env.CMUX_WORKSPACE_ID ?? "" });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return found;
|
|
27
|
+
}
|
|
28
|
+
async function listPanes() {
|
|
29
|
+
const tree = (await asked(["tree", "--all"]));
|
|
30
|
+
const found = [];
|
|
31
|
+
for (const window of tree.windows ?? []) {
|
|
32
|
+
for (const workspace of window.workspaces ?? []) {
|
|
33
|
+
for (const pane of workspace.panes ?? []) {
|
|
34
|
+
for (const surface of pane.surfaces ?? []) {
|
|
35
|
+
if (surface.type !== undefined && surface.type !== "terminal")
|
|
36
|
+
continue;
|
|
37
|
+
found.push({
|
|
38
|
+
id: surface.id,
|
|
39
|
+
tab: workspace.id,
|
|
40
|
+
tty: surface.tty || null,
|
|
41
|
+
command: null,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
async function neighbor(from, direction) {
|
|
50
|
+
const workspace = from.tab || env.CMUX_WORKSPACE_ID;
|
|
51
|
+
const rows = (await asked(["rpc", "pane.list", JSON.stringify(workspace ? { workspace_id: workspace } : {})])).panes ?? [];
|
|
52
|
+
const rects = [];
|
|
53
|
+
const surfacesOf = new Map();
|
|
54
|
+
for (const row of rows) {
|
|
55
|
+
const id = row.id ?? row.ref;
|
|
56
|
+
const frame = row.pixel_frame ?? row.frame;
|
|
57
|
+
if (!id || !frame || frame.x == null || frame.y == null || frame.width == null || frame.height == null)
|
|
58
|
+
continue;
|
|
59
|
+
rects.push({
|
|
60
|
+
id,
|
|
61
|
+
left: Math.round(frame.x),
|
|
62
|
+
top: Math.round(frame.y),
|
|
63
|
+
right: Math.round(frame.x + frame.width) - 1,
|
|
64
|
+
bottom: Math.round(frame.y + frame.height) - 1,
|
|
65
|
+
});
|
|
66
|
+
surfacesOf.set(id, (await asked(["list-pane-surfaces", "--pane", id])).surfaces ?? []);
|
|
67
|
+
}
|
|
68
|
+
const own = rects.find((rect) => (surfacesOf.get(rect.id) ?? []).some((surface) => surface.id === from.id));
|
|
69
|
+
if (!own)
|
|
70
|
+
return null;
|
|
71
|
+
const found = (0, shared_1.adjacentPane)(own, rects, direction, 16);
|
|
72
|
+
if (!found)
|
|
73
|
+
return null;
|
|
74
|
+
const surfaces = surfacesOf.get(found.id) ?? [];
|
|
75
|
+
const shown = surfaces.find((surface) => surface.focused || surface.selected) ?? surfaces[0];
|
|
76
|
+
return shown?.id ? { id: shown.id, tab: from.tab } : null;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
name: "cmux",
|
|
80
|
+
getCurrentPane: () => (0, shared_1.paneById)(panes, env.CMUX_SURFACE_ID),
|
|
81
|
+
listPanes,
|
|
82
|
+
neighbor,
|
|
83
|
+
async sendText(pane, text) {
|
|
84
|
+
await cmux(["rpc", "terminal.paste", JSON.stringify({ text, surface_id: pane, submit_key: "none" })]);
|
|
85
|
+
},
|
|
86
|
+
async focusPane(pane) {
|
|
87
|
+
await cmux(["focus-panel", "--panel", pane]);
|
|
88
|
+
},
|
|
89
|
+
async split({ from, direction, command }) {
|
|
90
|
+
const created = await asked(["new-split", direction, "--surface", from.id, "--focus", "true"]);
|
|
91
|
+
const opened = created.surface_id ?? created.surface_ref;
|
|
92
|
+
if (!opened)
|
|
93
|
+
throw new Error("cmux opened a split but did not say which surface it is");
|
|
94
|
+
await cmux(["send", "--surface", opened, "--", `${(0, shared_1.shellQuote)(command)}\\n`]);
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
exports.cmux = cmux;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const GHOSTTY_SCRIPT: string;
|