gpui-bridge 0.0.0-stage → 0.3.0

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.
@@ -0,0 +1,3 @@
1
+ // Protocol message types. Source of truth together with `host/src/protocol.rs`
2
+ // and PROTOCOL.md.
3
+ export const PROTOCOL_VERSION = 1;
package/dist/tree.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { ClientMessage, NodeId, NodeType } from "./protocol.js";
2
+ export type EventHandler = (payload: any) => void;
3
+ export declare class Tree {
4
+ private nextId;
5
+ private pending;
6
+ private readonly nodes;
7
+ /** Ids are positive, unique for the whole session and never reused. */
8
+ create(type: NodeType, props: Record<string, unknown>): NodeId;
9
+ type(id: NodeId): NodeType | undefined;
10
+ queue(op: ClientMessage): void;
11
+ /** Mirrors the host's move semantics so `remove` can drop descendant handlers. */
12
+ attach(parent: NodeId, child: NodeId): void;
13
+ remove(id: NodeId): void;
14
+ /** Sets or clears a handler; queues `setEvents` when the subscription set changes. */
15
+ setHandler(id: NodeId, event: string, handler: EventHandler | undefined): void;
16
+ handler(id: NodeId, event: string): EventHandler | undefined;
17
+ /** Sends every queued operation, then `commit`. */
18
+ commit(send: (message: ClientMessage) => void): void;
19
+ }
package/dist/tree.js ADDED
@@ -0,0 +1,70 @@
1
+ // Client-side bookkeeping: id allocation, batched operations and event handlers.
2
+ export class Tree {
3
+ nextId = 1;
4
+ pending = [];
5
+ nodes = new Map();
6
+ /** Ids are positive, unique for the whole session and never reused. */
7
+ create(type, props) {
8
+ const id = this.nextId++;
9
+ this.nodes.set(id, { type, children: new Set(), handlers: new Map() });
10
+ this.pending.push({ op: "create", id, type, props });
11
+ return id;
12
+ }
13
+ type(id) {
14
+ return this.nodes.get(id)?.type;
15
+ }
16
+ queue(op) {
17
+ this.pending.push(op);
18
+ }
19
+ /** Mirrors the host's move semantics so `remove` can drop descendant handlers. */
20
+ attach(parent, child) {
21
+ const record = this.nodes.get(child);
22
+ if (!record)
23
+ return;
24
+ if (record.parent !== undefined)
25
+ this.nodes.get(record.parent)?.children.delete(child);
26
+ record.parent = parent;
27
+ this.nodes.get(parent)?.children.add(child);
28
+ }
29
+ remove(id) {
30
+ const record = this.nodes.get(id);
31
+ if (!record)
32
+ return;
33
+ if (record.parent !== undefined)
34
+ this.nodes.get(record.parent)?.children.delete(id);
35
+ const stack = [id];
36
+ while (stack.length) {
37
+ const current = stack.pop();
38
+ const node = this.nodes.get(current);
39
+ if (!node)
40
+ continue;
41
+ stack.push(...node.children);
42
+ this.nodes.delete(current);
43
+ }
44
+ this.pending.push({ op: "remove", id });
45
+ }
46
+ /** Sets or clears a handler; queues `setEvents` when the subscription set changes. */
47
+ setHandler(id, event, handler) {
48
+ const record = this.nodes.get(id);
49
+ if (!record)
50
+ return;
51
+ const had = record.handlers.has(event);
52
+ if (handler)
53
+ record.handlers.set(event, handler);
54
+ else
55
+ record.handlers.delete(event);
56
+ if (had !== record.handlers.has(event)) {
57
+ this.pending.push({ op: "setEvents", id, events: [...record.handlers.keys()] });
58
+ }
59
+ }
60
+ handler(id, event) {
61
+ return this.nodes.get(id)?.handlers.get(event);
62
+ }
63
+ /** Sends every queued operation, then `commit`. */
64
+ commit(send) {
65
+ for (const op of this.pending)
66
+ send(op);
67
+ this.pending = [];
68
+ send({ op: "commit" });
69
+ }
70
+ }
@@ -0,0 +1,10 @@
1
+ import { type App } from "./app.js";
2
+ export * from "./app.js";
3
+ export * from "./protocol.js";
4
+ export interface TxikiAppOptions {
5
+ /** Host binary. Defaults to `$GPUI_BRIDGE_HOST`, then `gpui-bridge-host[.exe]` next to the running executable. */
6
+ hostPath?: string;
7
+ debug?: boolean;
8
+ headless?: boolean;
9
+ }
10
+ export declare function createTxikiApp(options?: TxikiAppOptions): Promise<App>;
package/dist/txiki.js ADDED
@@ -0,0 +1,90 @@
1
+ // txiki.js entry: the same client API as index.ts, over a `tjs.spawn` transport.
2
+ // Uses only txiki.js and Web APIs (no Node modules).
3
+ import { connect } from "./app.js";
4
+ export * from "./app.js";
5
+ export * from "./protocol.js";
6
+ const STDERR_TAIL_LINES = 20;
7
+ async function readLines(stream, onLine) {
8
+ const reader = stream.getReader();
9
+ const decoder = new TextDecoder();
10
+ let buffer = "";
11
+ for (;;) {
12
+ const { value, done } = await reader.read();
13
+ if (done)
14
+ break;
15
+ buffer += decoder.decode(value, { stream: true });
16
+ let newline;
17
+ while ((newline = buffer.indexOf("\n")) >= 0) {
18
+ const line = buffer.slice(0, newline).replace(/\r$/, "");
19
+ buffer = buffer.slice(newline + 1);
20
+ if (line)
21
+ onLine(line);
22
+ }
23
+ }
24
+ }
25
+ class TxikiHost {
26
+ process;
27
+ writer;
28
+ encoder = new TextEncoder();
29
+ exited = false;
30
+ constructor(path, args, debug, events) {
31
+ const stderrTail = [];
32
+ const finish = (exit) => {
33
+ if (this.exited)
34
+ return;
35
+ this.exited = true;
36
+ events.onExit({ ...exit, stderrTail });
37
+ };
38
+ try {
39
+ this.process = tjs.spawn([path, ...args], { stdin: "pipe", stdout: "pipe", stderr: "pipe" });
40
+ }
41
+ catch (error) {
42
+ // Reported asynchronously, like a failed spawn in Node.
43
+ queueMicrotask(() => finish({ code: null, signal: null, spawnError: error }));
44
+ return;
45
+ }
46
+ this.writer = this.process.stdin.getWriter();
47
+ const stdout = readLines(this.process.stdout, (line) => {
48
+ try {
49
+ events.onMessage(JSON.parse(line));
50
+ }
51
+ catch {
52
+ events.onCorruptLine(line);
53
+ }
54
+ });
55
+ const stderr = readLines(this.process.stderr, (line) => {
56
+ stderrTail.push(line);
57
+ if (stderrTail.length > STDERR_TAIL_LINES)
58
+ stderrTail.shift();
59
+ if (debug)
60
+ console.error(`[host] ${line}`);
61
+ });
62
+ // Wait for both streams so every line is delivered before the exit.
63
+ Promise.all([this.process.wait(), stdout, stderr]).then(([status]) => finish({ code: status.exit_status, signal: status.term_signal }));
64
+ }
65
+ send(message) {
66
+ if (this.exited || !this.writer)
67
+ return;
68
+ // Writes racing a dying host fail; the exit report carries the real cause.
69
+ this.writer.write(this.encoder.encode(JSON.stringify(message) + "\n")).catch(() => { });
70
+ }
71
+ close() {
72
+ this.writer?.close().catch(() => { });
73
+ }
74
+ kill() {
75
+ this.process?.kill();
76
+ }
77
+ }
78
+ function defaultHostPath() {
79
+ if (tjs.env.GPUI_BRIDGE_HOST)
80
+ return tjs.env.GPUI_BRIDGE_HOST;
81
+ // A compiled app ships the host next to its own executable.
82
+ const exe = tjs.exePath;
83
+ const name = exe.toLowerCase().endsWith(".exe") ? "gpui-bridge-host.exe" : "gpui-bridge-host";
84
+ return exe.replace(/[\\/][^\\/]*$/, "") + (exe.includes("\\") ? "\\" : "/") + name;
85
+ }
86
+ export function createTxikiApp(options = {}) {
87
+ const path = options.hostPath ?? defaultHostPath();
88
+ const args = options.headless ? ["--no-window"] : [];
89
+ return connect(path, (events) => new TxikiHost(path, args, options.debug ?? false, events));
90
+ }
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "gpui-bridge",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.3.0",
4
+ "description": "Node.js client for the gpui-bridge native UI host",
5
+ "license": "MIT",
6
+ "author": "gpui-bridge contributors",
7
+ "keywords": [
8
+ "gpui",
9
+ "gpui-component",
10
+ "native",
11
+ "desktop",
12
+ "ui",
13
+ "node",
14
+ "typescript"
15
+ ],
16
+ "homepage": "https://github.com/InDIOS/gpui-bridge#readme",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/InDIOS/gpui-bridge.git",
20
+ "directory": "packages/client-node"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/InDIOS/gpui-bridge/issues"
24
+ },
25
+ "type": "module",
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
28
+ "engines": {
29
+ "node": ">=22.6"
30
+ },
31
+ "optionalDependencies": {
32
+ "@indios/gpui-bridge-darwin-arm64": "0.3.0",
33
+ "@indios/gpui-bridge-linux-x64": "0.3.0",
34
+ "@indios/gpui-bridge-win32-x64": "0.3.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^26.6.5",
38
+ "typescript": "^7.0.2"
39
+ },
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "test": "tsc && node --test test/*.test.ts"
46
+ }
6
47
  }