@nativedesktop/react 0.1.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.
- package/dist/generated/rpc.js +13 -0
- package/dist/index.js +2089 -0
- package/dist/jsx-dev-runtime.js +6 -0
- package/dist/jsx-runtime.js +20 -0
- package/dist/packages/react/src/css-classes-validate.d.ts +3 -0
- package/dist/packages/react/src/dev-react.d.ts +23 -0
- package/dist/packages/react/src/dialogs.d.ts +54 -0
- package/dist/packages/react/src/errors.d.ts +55 -0
- package/dist/packages/react/src/generated/intrinsics.d.ts +1360 -0
- package/dist/packages/react/src/generated/protocol.d.ts +140 -0
- package/dist/packages/react/src/generated/rpc.d.ts +299 -0
- package/dist/packages/react/src/generated/schema-meta.d.ts +29 -0
- package/dist/packages/react/src/generated/widget-types.d.ts +2 -0
- package/dist/packages/react/src/hmr.d.ts +53 -0
- package/dist/packages/react/src/hooks.d.ts +4 -0
- package/dist/packages/react/src/host-config.d.ts +72 -0
- package/dist/packages/react/src/ids.d.ts +4 -0
- package/dist/packages/react/src/index.d.ts +26 -0
- package/dist/packages/react/src/jsx-dev-runtime.d.ts +2 -0
- package/dist/packages/react/src/jsx-runtime.d.ts +2 -0
- package/dist/packages/react/src/metrics.d.ts +12 -0
- package/dist/packages/react/src/native-component.d.ts +23 -0
- package/dist/packages/react/src/ops.d.ts +20 -0
- package/dist/packages/react/src/paths.d.ts +4 -0
- package/dist/packages/react/src/platform.d.ts +45 -0
- package/dist/packages/react/src/renderer.d.ts +12 -0
- package/dist/packages/react/src/shell.d.ts +6 -0
- package/dist/packages/react/src/store.d.ts +61 -0
- package/dist/packages/react/src/style-validate.d.ts +3 -0
- package/dist/packages/react/src/system.d.ts +166 -0
- package/dist/packages/react/src/toast.d.ts +19 -0
- package/dist/packages/react/src/webview.d.ts +5 -0
- package/dist/runtime/ndp-binary.d.ts +42 -0
- package/dist/runtime/ndp.d.ts +38 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2089 @@
|
|
|
1
|
+
// src/renderer.ts
|
|
2
|
+
import ReconcilerFactory from "react-reconciler";
|
|
3
|
+
import { ConcurrentRoot } from "react-reconciler/constants";
|
|
4
|
+
|
|
5
|
+
// src/generated/widget-types.ts
|
|
6
|
+
var WIDGET_TYPE = {
|
|
7
|
+
Window: 1,
|
|
8
|
+
Box: 2,
|
|
9
|
+
Label: 3,
|
|
10
|
+
Button: 4,
|
|
11
|
+
TextInput: 5,
|
|
12
|
+
TextArea: 6,
|
|
13
|
+
Checkbox: 7,
|
|
14
|
+
Radio: 8,
|
|
15
|
+
Select: 9,
|
|
16
|
+
Slider: 10,
|
|
17
|
+
ProgressBar: 11,
|
|
18
|
+
Image: 12,
|
|
19
|
+
ScrollView: 13,
|
|
20
|
+
Separator: 14,
|
|
21
|
+
Spinner: 15,
|
|
22
|
+
TabView: 16,
|
|
23
|
+
Grid: 17,
|
|
24
|
+
ListView: 18,
|
|
25
|
+
WebView: 19,
|
|
26
|
+
NativeView: 20,
|
|
27
|
+
SplitView: 21,
|
|
28
|
+
HeaderBar: 22,
|
|
29
|
+
ToolbarView: 23,
|
|
30
|
+
SearchInput: 24,
|
|
31
|
+
SourceList: 25,
|
|
32
|
+
SourceTree: 26,
|
|
33
|
+
Menubar: 27,
|
|
34
|
+
Menu: 28,
|
|
35
|
+
MenuItem: 29,
|
|
36
|
+
SettingsGroup: 30,
|
|
37
|
+
Row: 31,
|
|
38
|
+
SwitchRow: 32,
|
|
39
|
+
Clamp: 33,
|
|
40
|
+
Switch: 34,
|
|
41
|
+
ToggleButton: 35,
|
|
42
|
+
SegmentedControl: 36,
|
|
43
|
+
NumberInput: 37,
|
|
44
|
+
LinkButton: 38,
|
|
45
|
+
LevelIndicator: 39,
|
|
46
|
+
ColorPicker: 40,
|
|
47
|
+
Banner: 41,
|
|
48
|
+
MenuButton: 42,
|
|
49
|
+
SplitButton: 43,
|
|
50
|
+
Popover: 44,
|
|
51
|
+
Expander: 45,
|
|
52
|
+
StatusPage: 46,
|
|
53
|
+
ToastOverlay: 47,
|
|
54
|
+
DatePicker: 48,
|
|
55
|
+
Table: 49,
|
|
56
|
+
TreeView: 50,
|
|
57
|
+
FontPicker: 51,
|
|
58
|
+
Video: 52,
|
|
59
|
+
TrayItem: 53,
|
|
60
|
+
ShareButton: 54,
|
|
61
|
+
Terminal: 55,
|
|
62
|
+
Paned: 56,
|
|
63
|
+
CommandPalette: 57
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ../../runtime/ndp-binary.ts
|
|
67
|
+
class BinaryUnsupportedValue extends Error {
|
|
68
|
+
constructor(key, value) {
|
|
69
|
+
super(`ndp-binary: unsupported prop value for "${key}" (${typeof value}) — spec §5.3 has no array/object tag`);
|
|
70
|
+
this.name = "BinaryUnsupportedValue";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
var textEncoder = new TextEncoder;
|
|
74
|
+
|
|
75
|
+
class ByteWriter {
|
|
76
|
+
ab = new ArrayBuffer(256);
|
|
77
|
+
buf = new Uint8Array(this.ab);
|
|
78
|
+
view = new DataView(this.ab);
|
|
79
|
+
len = 0;
|
|
80
|
+
ensure(n) {
|
|
81
|
+
if (this.len + n <= this.ab.byteLength)
|
|
82
|
+
return;
|
|
83
|
+
let cap = this.ab.byteLength * 2;
|
|
84
|
+
while (cap < this.len + n)
|
|
85
|
+
cap *= 2;
|
|
86
|
+
const nextAb = new ArrayBuffer(cap);
|
|
87
|
+
new Uint8Array(nextAb).set(this.buf.subarray(0, this.len));
|
|
88
|
+
this.ab = nextAb;
|
|
89
|
+
this.buf = new Uint8Array(nextAb);
|
|
90
|
+
this.view = new DataView(nextAb);
|
|
91
|
+
}
|
|
92
|
+
u8(v) {
|
|
93
|
+
this.ensure(1);
|
|
94
|
+
this.buf[this.len++] = v & 255;
|
|
95
|
+
}
|
|
96
|
+
u16(v) {
|
|
97
|
+
this.ensure(2);
|
|
98
|
+
this.view.setUint16(this.len, v, true);
|
|
99
|
+
this.len += 2;
|
|
100
|
+
}
|
|
101
|
+
u32(v) {
|
|
102
|
+
this.ensure(4);
|
|
103
|
+
this.view.setUint32(this.len, v >>> 0, true);
|
|
104
|
+
this.len += 4;
|
|
105
|
+
}
|
|
106
|
+
i64(v) {
|
|
107
|
+
this.ensure(8);
|
|
108
|
+
this.view.setBigInt64(this.len, BigInt(v), true);
|
|
109
|
+
this.len += 8;
|
|
110
|
+
}
|
|
111
|
+
f64(v) {
|
|
112
|
+
this.ensure(8);
|
|
113
|
+
this.view.setFloat64(this.len, v, true);
|
|
114
|
+
this.len += 8;
|
|
115
|
+
}
|
|
116
|
+
bytes(b) {
|
|
117
|
+
this.ensure(b.length);
|
|
118
|
+
this.buf.set(b, this.len);
|
|
119
|
+
this.len += b.length;
|
|
120
|
+
}
|
|
121
|
+
lenString(s) {
|
|
122
|
+
this.ensure(4 + s.length * 3);
|
|
123
|
+
const lenPos = this.len;
|
|
124
|
+
const { written } = textEncoder.encodeInto(s, this.buf.subarray(this.len + 4));
|
|
125
|
+
this.view.setUint32(lenPos, written, true);
|
|
126
|
+
this.len += 4 + written;
|
|
127
|
+
}
|
|
128
|
+
slice() {
|
|
129
|
+
return this.buf.slice(0, this.len);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function encodeCommitBatchBinary(batch) {
|
|
133
|
+
const strings = [];
|
|
134
|
+
const index = new Map;
|
|
135
|
+
const intern = (s) => {
|
|
136
|
+
const hit = index.get(s);
|
|
137
|
+
if (hit !== undefined)
|
|
138
|
+
return hit;
|
|
139
|
+
const i = strings.length;
|
|
140
|
+
strings.push(s);
|
|
141
|
+
index.set(s, i);
|
|
142
|
+
return i;
|
|
143
|
+
};
|
|
144
|
+
const ops = new ByteWriter;
|
|
145
|
+
const writeProps = (props) => {
|
|
146
|
+
const keys = Object.keys(props);
|
|
147
|
+
ops.u16(keys.length);
|
|
148
|
+
for (const k of keys) {
|
|
149
|
+
ops.u32(intern(k));
|
|
150
|
+
const v = props[k];
|
|
151
|
+
if (v === null || v === undefined) {
|
|
152
|
+
ops.u8(0);
|
|
153
|
+
} else if (typeof v === "boolean") {
|
|
154
|
+
ops.u8(1);
|
|
155
|
+
ops.u8(v ? 1 : 0);
|
|
156
|
+
} else if (typeof v === "number") {
|
|
157
|
+
if (Number.isInteger(v)) {
|
|
158
|
+
ops.u8(2);
|
|
159
|
+
ops.i64(v);
|
|
160
|
+
} else {
|
|
161
|
+
ops.u8(3);
|
|
162
|
+
ops.f64(v);
|
|
163
|
+
}
|
|
164
|
+
} else if (typeof v === "string") {
|
|
165
|
+
ops.u8(4);
|
|
166
|
+
ops.u32(intern(v));
|
|
167
|
+
} else {
|
|
168
|
+
throw new BinaryUnsupportedValue(k, v);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
for (const op of batch.ops) {
|
|
173
|
+
switch (op.op) {
|
|
174
|
+
case "create": {
|
|
175
|
+
ops.u8(1);
|
|
176
|
+
ops.u32(op.id);
|
|
177
|
+
const wt = WIDGET_TYPE[op.widget];
|
|
178
|
+
if (wt === undefined)
|
|
179
|
+
throw new Error(`ndp-binary: unknown widget "${op.widget}"`);
|
|
180
|
+
ops.u16(wt);
|
|
181
|
+
writeProps(op.props);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "append":
|
|
185
|
+
ops.u8(2);
|
|
186
|
+
ops.u32(op.parent);
|
|
187
|
+
ops.u32(op.child);
|
|
188
|
+
break;
|
|
189
|
+
case "insertBefore":
|
|
190
|
+
ops.u8(3);
|
|
191
|
+
ops.u32(op.parent);
|
|
192
|
+
ops.u32(op.child);
|
|
193
|
+
ops.u32(op.before ?? 0);
|
|
194
|
+
break;
|
|
195
|
+
case "remove":
|
|
196
|
+
ops.u8(4);
|
|
197
|
+
ops.u32(op.id);
|
|
198
|
+
break;
|
|
199
|
+
case "setText":
|
|
200
|
+
ops.u8(5);
|
|
201
|
+
ops.u32(op.id);
|
|
202
|
+
ops.u32(intern(op.text));
|
|
203
|
+
break;
|
|
204
|
+
case "update":
|
|
205
|
+
ops.u8(6);
|
|
206
|
+
ops.u32(op.id);
|
|
207
|
+
writeProps(op.props);
|
|
208
|
+
break;
|
|
209
|
+
case "hide":
|
|
210
|
+
ops.u8(7);
|
|
211
|
+
ops.u32(op.id);
|
|
212
|
+
break;
|
|
213
|
+
case "unhide":
|
|
214
|
+
ops.u8(8);
|
|
215
|
+
ops.u32(op.id);
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const opBytes = ops.slice();
|
|
220
|
+
const stringTableOffset = 28 + opBytes.length;
|
|
221
|
+
const out = new ByteWriter;
|
|
222
|
+
out.u8(78);
|
|
223
|
+
out.u8(1);
|
|
224
|
+
for (let i = 0;i < 6; i++)
|
|
225
|
+
out.u8(0);
|
|
226
|
+
out.i64(batch.commitId);
|
|
227
|
+
out.u32(batch.generation);
|
|
228
|
+
out.u32(batch.ops.length);
|
|
229
|
+
out.u32(stringTableOffset);
|
|
230
|
+
out.bytes(opBytes);
|
|
231
|
+
out.u32(strings.length);
|
|
232
|
+
for (const s of strings)
|
|
233
|
+
out.lenString(s);
|
|
234
|
+
return out.slice();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/generated/protocol.ts
|
|
238
|
+
var NDP_VERSION = 1;
|
|
239
|
+
|
|
240
|
+
// ../../runtime/ndp.ts
|
|
241
|
+
var TRACE = process.env.NDP_TRACE === "1";
|
|
242
|
+
|
|
243
|
+
class Ndp {
|
|
244
|
+
socket;
|
|
245
|
+
inbox = new Uint8Array(0);
|
|
246
|
+
eventCb = null;
|
|
247
|
+
helloAckResolve = null;
|
|
248
|
+
outbox = [];
|
|
249
|
+
outboxOffset = 0;
|
|
250
|
+
encoding = "json";
|
|
251
|
+
backendName = "unknown";
|
|
252
|
+
hostWidgetsSet = null;
|
|
253
|
+
hostCommandsSet = null;
|
|
254
|
+
pending = new Map;
|
|
255
|
+
nextRequestId = 1;
|
|
256
|
+
systemEventCb = null;
|
|
257
|
+
constructor(socket) {
|
|
258
|
+
this.socket = socket;
|
|
259
|
+
}
|
|
260
|
+
get backend() {
|
|
261
|
+
return this.backendName;
|
|
262
|
+
}
|
|
263
|
+
get hostWidgets() {
|
|
264
|
+
return this.hostWidgetsSet;
|
|
265
|
+
}
|
|
266
|
+
get hostCommands() {
|
|
267
|
+
return this.hostCommandsSet;
|
|
268
|
+
}
|
|
269
|
+
static async connect() {
|
|
270
|
+
const path = process.env.ND_SOCKET;
|
|
271
|
+
if (!path)
|
|
272
|
+
throw new Error("ND_SOCKET not set");
|
|
273
|
+
let self;
|
|
274
|
+
const socket = await Bun.connect({
|
|
275
|
+
unix: path,
|
|
276
|
+
socket: {
|
|
277
|
+
data(_sock, chunk) {
|
|
278
|
+
self.onData(chunk);
|
|
279
|
+
},
|
|
280
|
+
close() {
|
|
281
|
+
for (const call of self.pending.values())
|
|
282
|
+
call.reject(new Error("ndp connection closed"));
|
|
283
|
+
self.pending.clear();
|
|
284
|
+
process.exit(0);
|
|
285
|
+
},
|
|
286
|
+
drain(sock) {
|
|
287
|
+
self.pump(sock);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
self = new Ndp(socket);
|
|
292
|
+
return self;
|
|
293
|
+
}
|
|
294
|
+
pump(sock) {
|
|
295
|
+
while (this.outbox.length > 0) {
|
|
296
|
+
const front = this.outbox[0];
|
|
297
|
+
const remaining = front.subarray(this.outboxOffset);
|
|
298
|
+
const n = sock.write(remaining);
|
|
299
|
+
if (n <= 0)
|
|
300
|
+
return;
|
|
301
|
+
this.outboxOffset += n;
|
|
302
|
+
if (this.outboxOffset >= front.length) {
|
|
303
|
+
this.outbox.shift();
|
|
304
|
+
this.outboxOffset = 0;
|
|
305
|
+
} else {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
onData(chunk) {
|
|
311
|
+
const merged = new Uint8Array(this.inbox.length + chunk.length);
|
|
312
|
+
merged.set(this.inbox, 0);
|
|
313
|
+
merged.set(chunk, this.inbox.length);
|
|
314
|
+
this.inbox = merged;
|
|
315
|
+
while (this.inbox.length >= 4) {
|
|
316
|
+
const view = new DataView(this.inbox.buffer, this.inbox.byteOffset, 4);
|
|
317
|
+
const len = view.getUint32(0, true);
|
|
318
|
+
if (this.inbox.length < 4 + len)
|
|
319
|
+
break;
|
|
320
|
+
const json = new TextDecoder().decode(this.inbox.subarray(4, 4 + len));
|
|
321
|
+
this.inbox = this.inbox.subarray(4 + len);
|
|
322
|
+
if (TRACE)
|
|
323
|
+
console.error("<< " + json);
|
|
324
|
+
this.dispatch(JSON.parse(json));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
dispatch(msg) {
|
|
328
|
+
if (msg.type === "helloAck") {
|
|
329
|
+
if (msg.ndpVersion !== NDP_VERSION)
|
|
330
|
+
throw new Error(`ndp mismatch: host ${msg.ndpVersion}`);
|
|
331
|
+
if (msg.encodings?.includes("binary") && process.env.ND_FORCE_JSON !== "1")
|
|
332
|
+
this.encoding = "binary";
|
|
333
|
+
this.backendName = msg.backend;
|
|
334
|
+
this.hostWidgetsSet = msg.hostWidgets ? new Set(msg.hostWidgets) : null;
|
|
335
|
+
this.hostCommandsSet = msg.hostCommands ? new Set(msg.hostCommands) : null;
|
|
336
|
+
this.helloAckResolve?.();
|
|
337
|
+
} else if (msg.type === "error") {
|
|
338
|
+
throw new Error(`host error: ${msg.message} (expected ${msg.expected}, got ${msg.got})`);
|
|
339
|
+
} else if (msg.type === "event") {
|
|
340
|
+
this.eventCb?.(msg);
|
|
341
|
+
} else if (msg.type === "systemResponse") {
|
|
342
|
+
const call = this.pending.get(msg.id);
|
|
343
|
+
if (!call)
|
|
344
|
+
return;
|
|
345
|
+
this.pending.delete(msg.id);
|
|
346
|
+
if (msg.ok)
|
|
347
|
+
call.resolve(msg.result);
|
|
348
|
+
else
|
|
349
|
+
call.reject(new Error(msg.errorMessage ?? "system request failed"));
|
|
350
|
+
} else if (msg.type === "systemEvent") {
|
|
351
|
+
this.systemEventCb?.(msg.channel, msg.data);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
send(obj) {
|
|
355
|
+
const json = new TextEncoder().encode(JSON.stringify(obj));
|
|
356
|
+
const frame = new Uint8Array(4 + json.length);
|
|
357
|
+
new DataView(frame.buffer).setUint32(0, json.length, true);
|
|
358
|
+
frame.set(json, 4);
|
|
359
|
+
if (TRACE)
|
|
360
|
+
console.error(">> " + new TextDecoder().decode(json));
|
|
361
|
+
const wasEmpty = this.outbox.length === 0;
|
|
362
|
+
this.outbox.push(frame);
|
|
363
|
+
if (wasEmpty)
|
|
364
|
+
this.pump(this.socket);
|
|
365
|
+
}
|
|
366
|
+
async handshake(runtime) {
|
|
367
|
+
const done = new Promise((res) => this.helloAckResolve = res);
|
|
368
|
+
this.send({ type: "hello", ndpVersion: NDP_VERSION, runtime });
|
|
369
|
+
await done;
|
|
370
|
+
}
|
|
371
|
+
sendCommit(batch) {
|
|
372
|
+
if (this.encoding === "binary") {
|
|
373
|
+
try {
|
|
374
|
+
this.sendBinaryFrame(encodeCommitBatchBinary(batch));
|
|
375
|
+
return;
|
|
376
|
+
} catch (err) {
|
|
377
|
+
if (!(err instanceof BinaryUnsupportedValue))
|
|
378
|
+
throw err;
|
|
379
|
+
if (TRACE)
|
|
380
|
+
console.error(`>> [binary fallback to json: ${err.message}]`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
this.send({ type: "commitBatch", ...batch });
|
|
384
|
+
}
|
|
385
|
+
sendBinaryFrame(payload) {
|
|
386
|
+
const frame = new Uint8Array(4 + payload.length);
|
|
387
|
+
new DataView(frame.buffer).setUint32(0, payload.length, true);
|
|
388
|
+
frame.set(payload, 4);
|
|
389
|
+
if (TRACE)
|
|
390
|
+
console.error(`>> [binary commitBatch ${payload.length}B]`);
|
|
391
|
+
const wasEmpty = this.outbox.length === 0;
|
|
392
|
+
this.outbox.push(frame);
|
|
393
|
+
if (wasEmpty)
|
|
394
|
+
this.pump(this.socket);
|
|
395
|
+
}
|
|
396
|
+
sendWidgetCommand(nodeId, command, arg = null) {
|
|
397
|
+
this.send({ type: "widgetCommand", nodeId, command, arg });
|
|
398
|
+
}
|
|
399
|
+
onEvent(cb) {
|
|
400
|
+
this.eventCb = cb;
|
|
401
|
+
}
|
|
402
|
+
request(method, params) {
|
|
403
|
+
const id = this.nextRequestId++;
|
|
404
|
+
return new Promise((resolve, reject) => {
|
|
405
|
+
this.pending.set(id, { resolve, reject });
|
|
406
|
+
this.send({ type: "systemRequest", id, method, params: params ?? {} });
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
onSystemEvent(cb) {
|
|
410
|
+
this.systemEventCb = cb;
|
|
411
|
+
}
|
|
412
|
+
ping() {
|
|
413
|
+
this.send({ type: "ping" });
|
|
414
|
+
}
|
|
415
|
+
sendRuntimeError(message, stack, fatal) {
|
|
416
|
+
this.send({ type: "runtimeError", message, stack, fatal });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/generated/schema-meta.ts
|
|
421
|
+
var intrinsicToName = {
|
|
422
|
+
window: "Window",
|
|
423
|
+
box: "Box",
|
|
424
|
+
label: "Label",
|
|
425
|
+
button: "Button",
|
|
426
|
+
textinput: "TextInput",
|
|
427
|
+
textarea: "TextArea",
|
|
428
|
+
checkbox: "Checkbox",
|
|
429
|
+
radio: "Radio",
|
|
430
|
+
select: "Select",
|
|
431
|
+
slider: "Slider",
|
|
432
|
+
progressbar: "ProgressBar",
|
|
433
|
+
image: "Image",
|
|
434
|
+
scrollview: "ScrollView",
|
|
435
|
+
separator: "Separator",
|
|
436
|
+
spinner: "Spinner",
|
|
437
|
+
tabview: "TabView",
|
|
438
|
+
grid: "Grid",
|
|
439
|
+
listview: "ListView",
|
|
440
|
+
webview: "WebView",
|
|
441
|
+
nativeview: "NativeView",
|
|
442
|
+
splitview: "SplitView",
|
|
443
|
+
headerbar: "HeaderBar",
|
|
444
|
+
toolbarview: "ToolbarView",
|
|
445
|
+
searchinput: "SearchInput",
|
|
446
|
+
sourcelist: "SourceList",
|
|
447
|
+
sourcetree: "SourceTree",
|
|
448
|
+
menubar: "Menubar",
|
|
449
|
+
menu: "Menu",
|
|
450
|
+
menuitem: "MenuItem",
|
|
451
|
+
settingsgroup: "SettingsGroup",
|
|
452
|
+
row: "Row",
|
|
453
|
+
switchrow: "SwitchRow",
|
|
454
|
+
clamp: "Clamp",
|
|
455
|
+
switch: "Switch",
|
|
456
|
+
togglebutton: "ToggleButton",
|
|
457
|
+
segmentedcontrol: "SegmentedControl",
|
|
458
|
+
numberinput: "NumberInput",
|
|
459
|
+
linkbutton: "LinkButton",
|
|
460
|
+
levelindicator: "LevelIndicator",
|
|
461
|
+
colorpicker: "ColorPicker",
|
|
462
|
+
banner: "Banner",
|
|
463
|
+
menubutton: "MenuButton",
|
|
464
|
+
splitbutton: "SplitButton",
|
|
465
|
+
popover: "Popover",
|
|
466
|
+
expander: "Expander",
|
|
467
|
+
statuspage: "StatusPage",
|
|
468
|
+
toastoverlay: "ToastOverlay",
|
|
469
|
+
datepicker: "DatePicker",
|
|
470
|
+
table: "Table",
|
|
471
|
+
treeview: "TreeView",
|
|
472
|
+
fontpicker: "FontPicker",
|
|
473
|
+
video: "Video",
|
|
474
|
+
trayitem: "TrayItem",
|
|
475
|
+
sharebutton: "ShareButton",
|
|
476
|
+
terminal: "Terminal",
|
|
477
|
+
paned: "Paned",
|
|
478
|
+
commandpalette: "CommandPalette"
|
|
479
|
+
};
|
|
480
|
+
var widgetEvents = {
|
|
481
|
+
window: [{ name: "alertResult", handler: "onAlertResult", payload: "data" }, { name: "openFileResult", handler: "onOpenFileResult", payload: "data" }, { name: "saveFileResult", handler: "onSaveFileResult", payload: "data" }, { name: "newTabRequested", handler: "onNewTabRequested", payload: "data" }, { name: "closed", handler: "onClosed", payload: "data" }, { name: "focused", handler: "onFocused", payload: "checked" }, { name: "sizeChanged", handler: "onSizeChanged", payload: "data" }],
|
|
482
|
+
box: [{ name: "hoverChanged", handler: "onHoverChanged", payload: "checked" }],
|
|
483
|
+
label: [],
|
|
484
|
+
button: [{ name: "clicked", handler: "onClick", payload: "none" }, { name: "hoverChanged", handler: "onHoverChanged", payload: "checked" }],
|
|
485
|
+
textinput: [{ name: "changed", handler: "onChanged", payload: "text" }, { name: "activate", handler: "onActivate", payload: "text" }],
|
|
486
|
+
textarea: [{ name: "changed", handler: "onChanged", payload: "text" }],
|
|
487
|
+
checkbox: [{ name: "toggled", handler: "onToggled", payload: "checked" }],
|
|
488
|
+
radio: [{ name: "toggled", handler: "onToggled", payload: "checked" }],
|
|
489
|
+
select: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "index" }],
|
|
490
|
+
slider: [{ name: "valueChanged", handler: "onValueChanged", payload: "value" }],
|
|
491
|
+
progressbar: [],
|
|
492
|
+
image: [],
|
|
493
|
+
scrollview: [],
|
|
494
|
+
separator: [],
|
|
495
|
+
spinner: [],
|
|
496
|
+
tabview: [],
|
|
497
|
+
grid: [],
|
|
498
|
+
listview: [{ name: "rowActivated", handler: "onRowActivated", payload: "index" }],
|
|
499
|
+
webview: [{ name: "navigate", handler: "onNavigate", payload: "text" }, { name: "titleChanged", handler: "onTitleChanged", payload: "text" }, { name: "loadingChanged", handler: "onLoadingChanged", payload: "checked" }, { name: "backAvailable", handler: "onBackAvailable", payload: "checked" }, { name: "forwardAvailable", handler: "onForwardAvailable", payload: "checked" }, { name: "loadProgress", handler: "onLoadProgress", payload: "value" }, { name: "loadFailed", handler: "onLoadFailed", payload: "data" }, { name: "newWindow", handler: "onNewWindow", payload: "text" }, { name: "downloadRequested", handler: "onDownloadRequested", payload: "data" }, { name: "javaScriptResult", handler: "onJavaScriptResult", payload: "data" }],
|
|
500
|
+
nativeview: [{ name: "nativeEvent", handler: "onNativeEvent", payload: "data" }],
|
|
501
|
+
splitview: [],
|
|
502
|
+
headerbar: [{ name: "back", handler: "onBack", payload: "none" }, { name: "forward", handler: "onForward", payload: "none" }],
|
|
503
|
+
toolbarview: [],
|
|
504
|
+
searchinput: [{ name: "changed", handler: "onChanged", payload: "text" }, { name: "activate", handler: "onActivate", payload: "text" }],
|
|
505
|
+
sourcelist: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "index" }, { name: "rowActivated", handler: "onRowActivated", payload: "index" }],
|
|
506
|
+
sourcetree: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "data" }, { name: "rowActivated", handler: "onRowActivated", payload: "data" }, { name: "nodeExpanded", handler: "onNodeExpanded", payload: "data" }, { name: "nodeCollapsed", handler: "onNodeCollapsed", payload: "data" }, { name: "actionClicked", handler: "onActionClicked", payload: "data" }],
|
|
507
|
+
menubar: [],
|
|
508
|
+
menu: [],
|
|
509
|
+
menuitem: [{ name: "selected", handler: "onSelect", payload: "none" }],
|
|
510
|
+
settingsgroup: [],
|
|
511
|
+
row: [{ name: "activated", handler: "onActivate", payload: "none" }],
|
|
512
|
+
switchrow: [{ name: "toggled", handler: "onToggled", payload: "checked" }],
|
|
513
|
+
clamp: [],
|
|
514
|
+
switch: [{ name: "toggled", handler: "onToggled", payload: "checked" }],
|
|
515
|
+
togglebutton: [{ name: "toggled", handler: "onToggled", payload: "checked" }],
|
|
516
|
+
segmentedcontrol: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "index" }],
|
|
517
|
+
numberinput: [{ name: "valueChanged", handler: "onValueChanged", payload: "value" }],
|
|
518
|
+
linkbutton: [{ name: "activate", handler: "onActivate", payload: "text" }],
|
|
519
|
+
levelindicator: [],
|
|
520
|
+
colorpicker: [{ name: "colorChanged", handler: "onColorChanged", payload: "text" }],
|
|
521
|
+
banner: [{ name: "buttonClicked", handler: "onButtonClicked", payload: "none" }],
|
|
522
|
+
menubutton: [],
|
|
523
|
+
splitbutton: [{ name: "clicked", handler: "onClick", payload: "none" }],
|
|
524
|
+
popover: [{ name: "closed", handler: "onClosed", payload: "none" }],
|
|
525
|
+
expander: [{ name: "toggled", handler: "onToggled", payload: "checked" }],
|
|
526
|
+
statuspage: [],
|
|
527
|
+
toastoverlay: [{ name: "toastButtonClicked", handler: "onToastButtonClicked", payload: "data" }, { name: "toastDismissed", handler: "onToastDismissed", payload: "data" }],
|
|
528
|
+
datepicker: [{ name: "dateChanged", handler: "onDateChanged", payload: "text" }],
|
|
529
|
+
table: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "index" }, { name: "rowActivated", handler: "onRowActivated", payload: "index" }, { name: "sortChanged", handler: "onSortChanged", payload: "data" }],
|
|
530
|
+
treeview: [{ name: "selectionChanged", handler: "onSelectionChanged", payload: "data" }, { name: "rowActivated", handler: "onRowActivated", payload: "data" }, { name: "nodeExpanded", handler: "onNodeExpanded", payload: "data" }, { name: "nodeCollapsed", handler: "onNodeCollapsed", payload: "data" }],
|
|
531
|
+
fontpicker: [{ name: "fontChanged", handler: "onFontChanged", payload: "text" }],
|
|
532
|
+
video: [],
|
|
533
|
+
trayitem: [],
|
|
534
|
+
sharebutton: [],
|
|
535
|
+
terminal: [{ name: "titleChanged", handler: "onTitleChanged", payload: "text" }, { name: "bell", handler: "onBell", payload: "none" }, { name: "exited", handler: "onExited", payload: "data" }, { name: "connectionState", handler: "onConnectionState", payload: "data" }, { name: "selectionChanged", handler: "onSelectionChanged", payload: "checked" }, { name: "imagePaste", handler: "onImagePaste", payload: "data" }, { name: "focusChanged", handler: "onFocusChanged", payload: "checked" }],
|
|
536
|
+
paned: [{ name: "positionChanged", handler: "onPositionChanged", payload: "position" }],
|
|
537
|
+
commandpalette: [{ name: "queryChanged", handler: "onQueryChanged", payload: "text" }, { name: "activate", handler: "onActivate", payload: "text" }, { name: "submit", handler: "onSubmit", payload: "text" }, { name: "cancel", handler: "onCancel", payload: "none" }]
|
|
538
|
+
};
|
|
539
|
+
var handlerPropNames = {
|
|
540
|
+
window: ["onAlertResult", "onOpenFileResult", "onSaveFileResult", "onNewTabRequested", "onClosed", "onFocused", "onSizeChanged"],
|
|
541
|
+
box: ["onHoverChanged"],
|
|
542
|
+
label: [],
|
|
543
|
+
button: ["onClick", "onHoverChanged"],
|
|
544
|
+
textinput: ["onChanged", "onActivate"],
|
|
545
|
+
textarea: ["onChanged"],
|
|
546
|
+
checkbox: ["onToggled"],
|
|
547
|
+
radio: ["onToggled"],
|
|
548
|
+
select: ["onSelectionChanged"],
|
|
549
|
+
slider: ["onValueChanged"],
|
|
550
|
+
progressbar: [],
|
|
551
|
+
image: [],
|
|
552
|
+
scrollview: [],
|
|
553
|
+
separator: [],
|
|
554
|
+
spinner: [],
|
|
555
|
+
tabview: [],
|
|
556
|
+
grid: [],
|
|
557
|
+
listview: ["onRowActivated"],
|
|
558
|
+
webview: ["onNavigate", "onTitleChanged", "onLoadingChanged", "onBackAvailable", "onForwardAvailable", "onLoadProgress", "onLoadFailed", "onNewWindow", "onDownloadRequested", "onJavaScriptResult"],
|
|
559
|
+
nativeview: ["onNativeEvent"],
|
|
560
|
+
splitview: [],
|
|
561
|
+
headerbar: ["onBack", "onForward"],
|
|
562
|
+
toolbarview: [],
|
|
563
|
+
searchinput: ["onChanged", "onActivate"],
|
|
564
|
+
sourcelist: ["onSelectionChanged", "onRowActivated"],
|
|
565
|
+
sourcetree: ["onSelectionChanged", "onRowActivated", "onNodeExpanded", "onNodeCollapsed", "onActionClicked"],
|
|
566
|
+
menubar: [],
|
|
567
|
+
menu: [],
|
|
568
|
+
menuitem: ["onSelect"],
|
|
569
|
+
settingsgroup: [],
|
|
570
|
+
row: ["onActivate"],
|
|
571
|
+
switchrow: ["onToggled"],
|
|
572
|
+
clamp: [],
|
|
573
|
+
switch: ["onToggled"],
|
|
574
|
+
togglebutton: ["onToggled"],
|
|
575
|
+
segmentedcontrol: ["onSelectionChanged"],
|
|
576
|
+
numberinput: ["onValueChanged"],
|
|
577
|
+
linkbutton: ["onActivate"],
|
|
578
|
+
levelindicator: [],
|
|
579
|
+
colorpicker: ["onColorChanged"],
|
|
580
|
+
banner: ["onButtonClicked"],
|
|
581
|
+
menubutton: [],
|
|
582
|
+
splitbutton: ["onClick"],
|
|
583
|
+
popover: ["onClosed"],
|
|
584
|
+
expander: ["onToggled"],
|
|
585
|
+
statuspage: [],
|
|
586
|
+
toastoverlay: ["onToastButtonClicked", "onToastDismissed"],
|
|
587
|
+
datepicker: ["onDateChanged"],
|
|
588
|
+
table: ["onSelectionChanged", "onRowActivated", "onSortChanged"],
|
|
589
|
+
treeview: ["onSelectionChanged", "onRowActivated", "onNodeExpanded", "onNodeCollapsed"],
|
|
590
|
+
fontpicker: ["onFontChanged"],
|
|
591
|
+
video: [],
|
|
592
|
+
trayitem: [],
|
|
593
|
+
sharebutton: [],
|
|
594
|
+
terminal: ["onTitleChanged", "onBell", "onExited", "onConnectionState", "onSelectionChanged", "onImagePaste", "onFocusChanged"],
|
|
595
|
+
paned: ["onPositionChanged"],
|
|
596
|
+
commandpalette: ["onQueryChanged", "onActivate", "onSubmit", "onCancel"]
|
|
597
|
+
};
|
|
598
|
+
var widgetCommands = {
|
|
599
|
+
window: ["showAlert", "openFile", "saveFile", "showAbout", "showTabOverview", "present"],
|
|
600
|
+
box: [],
|
|
601
|
+
label: [],
|
|
602
|
+
button: [],
|
|
603
|
+
textinput: [],
|
|
604
|
+
textarea: [],
|
|
605
|
+
checkbox: [],
|
|
606
|
+
radio: [],
|
|
607
|
+
select: [],
|
|
608
|
+
slider: [],
|
|
609
|
+
progressbar: [],
|
|
610
|
+
image: [],
|
|
611
|
+
scrollview: [],
|
|
612
|
+
separator: [],
|
|
613
|
+
spinner: [],
|
|
614
|
+
tabview: [],
|
|
615
|
+
grid: [],
|
|
616
|
+
listview: [],
|
|
617
|
+
webview: ["goBack", "goForward", "reload", "stop", "executeJavaScript", "setZoom", "setUserAgent", "openDevTools"],
|
|
618
|
+
nativeview: [],
|
|
619
|
+
splitview: [],
|
|
620
|
+
headerbar: [],
|
|
621
|
+
toolbarview: [],
|
|
622
|
+
searchinput: [],
|
|
623
|
+
sourcelist: [],
|
|
624
|
+
sourcetree: [],
|
|
625
|
+
menubar: [],
|
|
626
|
+
menu: [],
|
|
627
|
+
menuitem: [],
|
|
628
|
+
settingsgroup: [],
|
|
629
|
+
row: [],
|
|
630
|
+
switchrow: [],
|
|
631
|
+
clamp: [],
|
|
632
|
+
switch: [],
|
|
633
|
+
togglebutton: [],
|
|
634
|
+
segmentedcontrol: [],
|
|
635
|
+
numberinput: [],
|
|
636
|
+
linkbutton: [],
|
|
637
|
+
levelindicator: [],
|
|
638
|
+
colorpicker: [],
|
|
639
|
+
banner: [],
|
|
640
|
+
menubutton: [],
|
|
641
|
+
splitbutton: [],
|
|
642
|
+
popover: [],
|
|
643
|
+
expander: [],
|
|
644
|
+
statuspage: [],
|
|
645
|
+
toastoverlay: ["showToast", "dismissToast"],
|
|
646
|
+
datepicker: [],
|
|
647
|
+
table: [],
|
|
648
|
+
treeview: [],
|
|
649
|
+
fontpicker: [],
|
|
650
|
+
video: [],
|
|
651
|
+
trayitem: [],
|
|
652
|
+
sharebutton: [],
|
|
653
|
+
terminal: ["copy", "paste", "selectAll", "clearSelection", "focus"],
|
|
654
|
+
paned: [],
|
|
655
|
+
commandpalette: []
|
|
656
|
+
};
|
|
657
|
+
var widgetPlatforms = {
|
|
658
|
+
window: null,
|
|
659
|
+
box: null,
|
|
660
|
+
label: null,
|
|
661
|
+
button: null,
|
|
662
|
+
textinput: null,
|
|
663
|
+
textarea: null,
|
|
664
|
+
checkbox: null,
|
|
665
|
+
radio: null,
|
|
666
|
+
select: null,
|
|
667
|
+
slider: null,
|
|
668
|
+
progressbar: null,
|
|
669
|
+
image: null,
|
|
670
|
+
scrollview: null,
|
|
671
|
+
separator: null,
|
|
672
|
+
spinner: null,
|
|
673
|
+
tabview: null,
|
|
674
|
+
grid: null,
|
|
675
|
+
listview: null,
|
|
676
|
+
webview: null,
|
|
677
|
+
nativeview: null,
|
|
678
|
+
splitview: null,
|
|
679
|
+
headerbar: null,
|
|
680
|
+
toolbarview: null,
|
|
681
|
+
searchinput: null,
|
|
682
|
+
sourcelist: null,
|
|
683
|
+
sourcetree: null,
|
|
684
|
+
menubar: null,
|
|
685
|
+
menu: null,
|
|
686
|
+
menuitem: null,
|
|
687
|
+
settingsgroup: null,
|
|
688
|
+
row: null,
|
|
689
|
+
switchrow: null,
|
|
690
|
+
clamp: null,
|
|
691
|
+
switch: null,
|
|
692
|
+
togglebutton: null,
|
|
693
|
+
segmentedcontrol: null,
|
|
694
|
+
numberinput: null,
|
|
695
|
+
linkbutton: null,
|
|
696
|
+
levelindicator: null,
|
|
697
|
+
colorpicker: null,
|
|
698
|
+
banner: null,
|
|
699
|
+
menubutton: null,
|
|
700
|
+
splitbutton: null,
|
|
701
|
+
popover: null,
|
|
702
|
+
expander: null,
|
|
703
|
+
statuspage: null,
|
|
704
|
+
toastoverlay: null,
|
|
705
|
+
datepicker: null,
|
|
706
|
+
table: null,
|
|
707
|
+
treeview: null,
|
|
708
|
+
fontpicker: null,
|
|
709
|
+
video: null,
|
|
710
|
+
trayitem: ["macos"],
|
|
711
|
+
sharebutton: ["macos"],
|
|
712
|
+
terminal: null,
|
|
713
|
+
paned: null,
|
|
714
|
+
commandpalette: null
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
// src/host-config.ts
|
|
718
|
+
import { DiscreteEventPriority, ContinuousEventPriority, DefaultEventPriority } from "react-reconciler/constants";
|
|
719
|
+
|
|
720
|
+
// src/ids.ts
|
|
721
|
+
var generation = 0;
|
|
722
|
+
var seq = 0;
|
|
723
|
+
function currentGeneration() {
|
|
724
|
+
return generation;
|
|
725
|
+
}
|
|
726
|
+
function newGeneration() {
|
|
727
|
+
generation += 1;
|
|
728
|
+
seq = 0;
|
|
729
|
+
}
|
|
730
|
+
function nextNodeId() {
|
|
731
|
+
seq += 1;
|
|
732
|
+
return generation << 24 | seq & 16777215;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// src/ops.ts
|
|
736
|
+
class Batch {
|
|
737
|
+
ops = [];
|
|
738
|
+
push(op) {
|
|
739
|
+
this.ops.push(op);
|
|
740
|
+
}
|
|
741
|
+
drain() {
|
|
742
|
+
const o = this.ops;
|
|
743
|
+
this.ops = [];
|
|
744
|
+
return o;
|
|
745
|
+
}
|
|
746
|
+
get length() {
|
|
747
|
+
return this.ops.length;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
class NodeRegistry {
|
|
752
|
+
byId = new Map;
|
|
753
|
+
register(rec) {
|
|
754
|
+
this.byId.set(rec.id, rec);
|
|
755
|
+
}
|
|
756
|
+
get(id) {
|
|
757
|
+
return this.byId.get(id);
|
|
758
|
+
}
|
|
759
|
+
unregister(id) {
|
|
760
|
+
this.byId.delete(id);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/generated/intrinsics.ts
|
|
765
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
766
|
+
var styleKeySpec = {
|
|
767
|
+
background: null,
|
|
768
|
+
color: null,
|
|
769
|
+
font: ["fontSize", "fontWeight", "fontFamily"],
|
|
770
|
+
padding: null,
|
|
771
|
+
margin: null,
|
|
772
|
+
hexpand: null,
|
|
773
|
+
vexpand: null,
|
|
774
|
+
halign: null,
|
|
775
|
+
valign: null,
|
|
776
|
+
border: ["borderWidth", "borderColor", "borderRadius"]
|
|
777
|
+
};
|
|
778
|
+
var cssClassSpec = ["suggested-action", "destructive-action", "flat", "raised", "circular", "pill", "linked", "toolbar", "spacer", "title-1", "title-2", "title-3", "title-4", "heading", "document", "body", "caption-heading", "caption", "monospace", "numeric", "accent", "success", "warning", "error", "boxed-list", "boxed-list-separate", "card", "activatable", "navigation-sidebar", "nd-native-sidebar", "selection-mode", "osd", "dimmed", "background", "view", "frame", "compact", "menu", "inline", "large-title", "property", "round", "opaque", "devel", "icon-dropshadow", "lowres-icon"];
|
|
779
|
+
|
|
780
|
+
// src/style-validate.ts
|
|
781
|
+
class StyleError extends Error {
|
|
782
|
+
}
|
|
783
|
+
var validTop = Object.keys(styleKeySpec);
|
|
784
|
+
function nearest(bad, valid) {
|
|
785
|
+
let best = null, bestD = Infinity;
|
|
786
|
+
for (const v of valid) {
|
|
787
|
+
const d = lev(bad, v);
|
|
788
|
+
if (d < bestD) {
|
|
789
|
+
bestD = d;
|
|
790
|
+
best = v;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return bestD <= 3 ? best : null;
|
|
794
|
+
}
|
|
795
|
+
function lev(a, b) {
|
|
796
|
+
const m = a.length, n = b.length;
|
|
797
|
+
const dp = Array.from({ length: m + 1 }, (_, i) => [i, ...Array(n).fill(0)]);
|
|
798
|
+
for (let j = 0;j <= n; j++)
|
|
799
|
+
dp[0][j] = j;
|
|
800
|
+
for (let i = 1;i <= m; i++)
|
|
801
|
+
for (let j = 1;j <= n; j++)
|
|
802
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
803
|
+
return dp[m][n];
|
|
804
|
+
}
|
|
805
|
+
function reject(bad, valid, where) {
|
|
806
|
+
const near = nearest(bad, valid);
|
|
807
|
+
const hint = near ? `Did you mean "${near}"?` : `Valid keys: ${valid.join(", ")}.`;
|
|
808
|
+
throw new StyleError(`Invalid style key "${bad}"${where} — GTK styling is not web CSS. ${hint} See docs/styling.md`);
|
|
809
|
+
}
|
|
810
|
+
function validateStyle(style) {
|
|
811
|
+
if (style == null || typeof style !== "object")
|
|
812
|
+
return;
|
|
813
|
+
for (const [k, v] of Object.entries(style)) {
|
|
814
|
+
if (!(k in styleKeySpec))
|
|
815
|
+
reject(k, validTop, "");
|
|
816
|
+
const nested = styleKeySpec[k];
|
|
817
|
+
if (nested && v != null && typeof v === "object" && !Array.isArray(v)) {
|
|
818
|
+
for (const nk of Object.keys(v)) {
|
|
819
|
+
if (!nested.includes(nk))
|
|
820
|
+
reject(nk, nested, ` in "${k}"`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/css-classes-validate.ts
|
|
827
|
+
class CssClassError extends Error {
|
|
828
|
+
}
|
|
829
|
+
function nearest2(bad, valid) {
|
|
830
|
+
let best = null, bestD = Infinity;
|
|
831
|
+
for (const v of valid) {
|
|
832
|
+
const d = lev2(bad, v);
|
|
833
|
+
if (d < bestD) {
|
|
834
|
+
bestD = d;
|
|
835
|
+
best = v;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return bestD <= 3 ? best : null;
|
|
839
|
+
}
|
|
840
|
+
function lev2(a, b) {
|
|
841
|
+
const m = a.length, n = b.length;
|
|
842
|
+
const dp = Array.from({ length: m + 1 }, (_, i) => [i, ...Array(n).fill(0)]);
|
|
843
|
+
for (let j = 0;j <= n; j++)
|
|
844
|
+
dp[0][j] = j;
|
|
845
|
+
for (let i = 1;i <= m; i++)
|
|
846
|
+
for (let j = 1;j <= n; j++)
|
|
847
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
848
|
+
return dp[m][n];
|
|
849
|
+
}
|
|
850
|
+
function validateCssClasses(classes) {
|
|
851
|
+
if (classes == null)
|
|
852
|
+
return;
|
|
853
|
+
if (!Array.isArray(classes))
|
|
854
|
+
throw new CssClassError(`cssClasses must be a string[] — got ${typeof classes}. See docs/styling.md`);
|
|
855
|
+
for (const c of classes) {
|
|
856
|
+
if (typeof c !== "string")
|
|
857
|
+
throw new CssClassError(`cssClasses entries must be strings — got ${typeof c}. See docs/styling.md`);
|
|
858
|
+
if (!cssClassSpec.includes(c)) {
|
|
859
|
+
const near = nearest2(c, cssClassSpec);
|
|
860
|
+
const hint = near ? `Did you mean "${near}"?` : `Valid classes: ${cssClassSpec.join(", ")}.`;
|
|
861
|
+
throw new CssClassError(`Unknown CSS class "${c}" — only Adwaita/GTK design-system classes are allowed. ${hint} See docs/styling.md`);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/hmr.ts
|
|
867
|
+
import * as RefreshRuntimeFirstEval from "react-refresh/runtime";
|
|
868
|
+
function pinnedRefreshRuntime() {
|
|
869
|
+
if (!globalThis.__nd_refresh_runtime)
|
|
870
|
+
globalThis.__nd_refresh_runtime = RefreshRuntimeFirstEval;
|
|
871
|
+
return globalThis.__nd_refresh_runtime;
|
|
872
|
+
}
|
|
873
|
+
function getHmrState() {
|
|
874
|
+
return globalThis.__nd_hmr;
|
|
875
|
+
}
|
|
876
|
+
function setHmrState(s) {
|
|
877
|
+
globalThis.__nd_hmr = s;
|
|
878
|
+
}
|
|
879
|
+
function isHot() {
|
|
880
|
+
return process.env.ND_DEV === "1";
|
|
881
|
+
}
|
|
882
|
+
var refreshReady = false;
|
|
883
|
+
function setupRefresh(reconciler) {
|
|
884
|
+
if (refreshReady)
|
|
885
|
+
return;
|
|
886
|
+
pinnedRefreshRuntime().injectIntoGlobalHook(globalThis);
|
|
887
|
+
globalThis.$RefreshReg$ = (type, id) => {
|
|
888
|
+
pinnedRefreshRuntime().register(type, id);
|
|
889
|
+
};
|
|
890
|
+
globalThis.$RefreshSig$ = () => pinnedRefreshRuntime().createSignatureFunctionForTransform();
|
|
891
|
+
reconciler.setRefreshHandler?.((type) => pinnedRefreshRuntime().getFamilyByType(type));
|
|
892
|
+
reconciler.injectIntoDevTools?.();
|
|
893
|
+
refreshReady = true;
|
|
894
|
+
}
|
|
895
|
+
function performRefresh() {
|
|
896
|
+
pinnedRefreshRuntime().performReactRefresh();
|
|
897
|
+
}
|
|
898
|
+
function registerExports(mod, moduleId) {
|
|
899
|
+
for (const [name, val] of Object.entries(mod)) {
|
|
900
|
+
if (typeof val === "function" && /^[A-Z]/.test(name)) {
|
|
901
|
+
pinnedRefreshRuntime().register(val, `${moduleId} ${name}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
var ROOT_FAMILY_KEY = "nd-root";
|
|
906
|
+
function registerRoot(rootComponentType) {
|
|
907
|
+
pinnedRefreshRuntime().register(rootComponentType, ROOT_FAMILY_KEY);
|
|
908
|
+
}
|
|
909
|
+
function hotUpdateRoot(rootComponentType) {
|
|
910
|
+
pinnedRefreshRuntime().register(rootComponentType, ROOT_FAMILY_KEY);
|
|
911
|
+
pinnedRefreshRuntime().performReactRefresh();
|
|
912
|
+
}
|
|
913
|
+
function fullReload() {
|
|
914
|
+
newGeneration();
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// src/platform.ts
|
|
918
|
+
function state() {
|
|
919
|
+
if (!globalThis.__nd_platform) {
|
|
920
|
+
globalThis.__nd_platform = { backend: "unknown", hostWidgets: null, hostCommands: null };
|
|
921
|
+
}
|
|
922
|
+
return globalThis.__nd_platform;
|
|
923
|
+
}
|
|
924
|
+
var backendListeners = [];
|
|
925
|
+
function onBackendKnown(cb) {
|
|
926
|
+
backendListeners.push(cb);
|
|
927
|
+
if (state().backend !== "unknown")
|
|
928
|
+
cb();
|
|
929
|
+
}
|
|
930
|
+
function setBackend(name) {
|
|
931
|
+
state().backend = name;
|
|
932
|
+
for (const cb of backendListeners)
|
|
933
|
+
cb();
|
|
934
|
+
}
|
|
935
|
+
function setHostManifest(widgets, commands) {
|
|
936
|
+
state().hostWidgets = widgets;
|
|
937
|
+
state().hostCommands = commands;
|
|
938
|
+
}
|
|
939
|
+
function hasWidget(type) {
|
|
940
|
+
const { hostWidgets } = state();
|
|
941
|
+
if (hostWidgets)
|
|
942
|
+
return hostWidgets.has(type);
|
|
943
|
+
return type in intrinsicToName;
|
|
944
|
+
}
|
|
945
|
+
function hasCommand(type, command) {
|
|
946
|
+
const { hostCommands } = state();
|
|
947
|
+
if (hostCommands)
|
|
948
|
+
return hostCommands.has(`${type}.${command}`);
|
|
949
|
+
return (widgetCommands[type] ?? []).includes(command);
|
|
950
|
+
}
|
|
951
|
+
function currentOS() {
|
|
952
|
+
switch (process.platform) {
|
|
953
|
+
case "darwin":
|
|
954
|
+
return "macos";
|
|
955
|
+
case "win32":
|
|
956
|
+
return "windows";
|
|
957
|
+
default:
|
|
958
|
+
return "linux";
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
var Platform = {
|
|
962
|
+
get backend() {
|
|
963
|
+
return state().backend;
|
|
964
|
+
},
|
|
965
|
+
get os() {
|
|
966
|
+
return currentOS();
|
|
967
|
+
},
|
|
968
|
+
select(spec) {
|
|
969
|
+
const { backend } = state();
|
|
970
|
+
return backend in spec ? spec[backend] : spec.default;
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
// src/host-config.ts
|
|
975
|
+
var activeBatch = new Batch;
|
|
976
|
+
var registry = new NodeRegistry;
|
|
977
|
+
function bindCommitTargets(b, r) {
|
|
978
|
+
activeBatch = b;
|
|
979
|
+
registry = r;
|
|
980
|
+
}
|
|
981
|
+
var currentUpdatePriority = DefaultEventPriority;
|
|
982
|
+
function textOf(children) {
|
|
983
|
+
if (typeof children === "string")
|
|
984
|
+
return children;
|
|
985
|
+
if (typeof children === "number")
|
|
986
|
+
return String(children);
|
|
987
|
+
if (Array.isArray(children) && children.every((c) => typeof c === "string" || typeof c === "number"))
|
|
988
|
+
return children.join("");
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
function collectHandlers(type, props) {
|
|
992
|
+
const out = {};
|
|
993
|
+
for (const ev of widgetEvents[type] ?? []) {
|
|
994
|
+
const h = props[ev.handler];
|
|
995
|
+
if (typeof h === "function")
|
|
996
|
+
out[ev.name] = h;
|
|
997
|
+
}
|
|
998
|
+
return out;
|
|
999
|
+
}
|
|
1000
|
+
function emitCreateIfNew(inst) {
|
|
1001
|
+
if (registry.get(inst.id))
|
|
1002
|
+
return;
|
|
1003
|
+
const props = { ...inst.props };
|
|
1004
|
+
if ("style" in props)
|
|
1005
|
+
validateStyle(props.style);
|
|
1006
|
+
if ("cssClasses" in props)
|
|
1007
|
+
validateCssClasses(props.cssClasses);
|
|
1008
|
+
const text = textOf(props.children);
|
|
1009
|
+
if (inst.type === "label" && text !== undefined)
|
|
1010
|
+
props.text = text;
|
|
1011
|
+
delete props.children;
|
|
1012
|
+
for (const h of handlerPropNames[inst.type] ?? [])
|
|
1013
|
+
delete props[h];
|
|
1014
|
+
activeBatch.push({ op: "create", id: inst.id, widget: intrinsicToName[inst.type], props });
|
|
1015
|
+
registry.register({ id: inst.id, type: inst.type, props: inst.props, handlers: collectHandlers(inst.type, inst.props) });
|
|
1016
|
+
for (const child of inst.children) {
|
|
1017
|
+
emitCreateIfNew(child);
|
|
1018
|
+
activeBatch.push({ op: "append", parent: inst.id, child: child.id });
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function setPriorityFor(kind) {
|
|
1022
|
+
currentUpdatePriority = kind === "discrete" ? DiscreteEventPriority : kind === "continuous" ? ContinuousEventPriority : DefaultEventPriority;
|
|
1023
|
+
}
|
|
1024
|
+
var PLATFORM_LABEL = { macos: "macOS", linux: "Linux" };
|
|
1025
|
+
var warnedPlatformMismatch = new Set;
|
|
1026
|
+
function checkPlatform(type) {
|
|
1027
|
+
if (!isHot() || warnedPlatformMismatch.has(type))
|
|
1028
|
+
return;
|
|
1029
|
+
const allowed = widgetPlatforms[type];
|
|
1030
|
+
if (!allowed || allowed.includes(Platform.os))
|
|
1031
|
+
return;
|
|
1032
|
+
warnedPlatformMismatch.add(type);
|
|
1033
|
+
const label = allowed.map((p) => PLATFORM_LABEL[p]).join("/");
|
|
1034
|
+
console.warn(`<${type}> is ${label}-only; it renders nothing on ${Platform.os}. Gate it with Platform.os.`);
|
|
1035
|
+
}
|
|
1036
|
+
var hostConfig = {
|
|
1037
|
+
supportsMutation: true,
|
|
1038
|
+
supportsPersistence: false,
|
|
1039
|
+
supportsHydration: false,
|
|
1040
|
+
isPrimaryRenderer: true,
|
|
1041
|
+
noTimeout: -1,
|
|
1042
|
+
supportsMicrotasks: true,
|
|
1043
|
+
scheduleMicrotask: (fn) => queueMicrotask(fn),
|
|
1044
|
+
scheduleTimeout: (fn, delay) => setTimeout(fn, delay),
|
|
1045
|
+
cancelTimeout: (id) => clearTimeout(id),
|
|
1046
|
+
getRootHostContext: () => ({ root: true }),
|
|
1047
|
+
getChildHostContext: (parent) => parent,
|
|
1048
|
+
prepareForCommit: () => null,
|
|
1049
|
+
resetAfterCommit: () => {},
|
|
1050
|
+
clearContainer: () => {},
|
|
1051
|
+
preparePortalMount: () => {},
|
|
1052
|
+
createInstance(type, props) {
|
|
1053
|
+
checkPlatform(type);
|
|
1054
|
+
const id = nextNodeId();
|
|
1055
|
+
return { id, type, props, children: [] };
|
|
1056
|
+
},
|
|
1057
|
+
createTextInstance(text) {
|
|
1058
|
+
return { text };
|
|
1059
|
+
},
|
|
1060
|
+
shouldSetTextContent: (type) => type === "label",
|
|
1061
|
+
appendInitialChild(parent, child) {
|
|
1062
|
+
parent.children.push(child);
|
|
1063
|
+
},
|
|
1064
|
+
finalizeInitialChildren: () => false,
|
|
1065
|
+
appendChild(parent, child) {
|
|
1066
|
+
emitCreateIfNew(child);
|
|
1067
|
+
activeBatch.push({ op: "append", parent: parent.id, child: child.id });
|
|
1068
|
+
},
|
|
1069
|
+
appendChildToContainer(_container, child) {
|
|
1070
|
+
emitCreateIfNew(child);
|
|
1071
|
+
},
|
|
1072
|
+
insertBefore(parent, child, before) {
|
|
1073
|
+
emitCreateIfNew(child);
|
|
1074
|
+
activeBatch.push({ op: "insertBefore", parent: parent.id, child: child.id, before: before.id });
|
|
1075
|
+
},
|
|
1076
|
+
insertInContainerBefore(_c, child, _b) {
|
|
1077
|
+
emitCreateIfNew(child);
|
|
1078
|
+
},
|
|
1079
|
+
removeChild(_parent, child) {
|
|
1080
|
+
activeBatch.push({ op: "remove", id: child.id });
|
|
1081
|
+
registry.unregister(child.id);
|
|
1082
|
+
},
|
|
1083
|
+
removeChildFromContainer(_c, child) {
|
|
1084
|
+
activeBatch.push({ op: "remove", id: child.id });
|
|
1085
|
+
registry.unregister(child.id);
|
|
1086
|
+
},
|
|
1087
|
+
commitUpdate(inst, type, oldProps, newProps) {
|
|
1088
|
+
if ("style" in newProps)
|
|
1089
|
+
validateStyle(newProps.style);
|
|
1090
|
+
if ("cssClasses" in newProps)
|
|
1091
|
+
validateCssClasses(newProps.cssClasses);
|
|
1092
|
+
if (type === "label") {
|
|
1093
|
+
const t = textOf(newProps.children) ?? newProps.text;
|
|
1094
|
+
const old = textOf(oldProps.children) ?? oldProps.text;
|
|
1095
|
+
if (t !== undefined && t !== old)
|
|
1096
|
+
activeBatch.push({ op: "setText", id: inst.id, text: t });
|
|
1097
|
+
}
|
|
1098
|
+
const skip = handlerPropNames[type] ?? [];
|
|
1099
|
+
const changed = {};
|
|
1100
|
+
for (const k of Object.keys(newProps)) {
|
|
1101
|
+
if (k === "children" || skip.includes(k))
|
|
1102
|
+
continue;
|
|
1103
|
+
if (type === "label" && k === "text")
|
|
1104
|
+
continue;
|
|
1105
|
+
if (newProps[k] !== oldProps[k])
|
|
1106
|
+
changed[k] = newProps[k];
|
|
1107
|
+
}
|
|
1108
|
+
if (Object.keys(changed).length)
|
|
1109
|
+
activeBatch.push({ op: "update", id: inst.id, props: changed });
|
|
1110
|
+
inst.props = newProps;
|
|
1111
|
+
const rec = registry.get(inst.id);
|
|
1112
|
+
if (rec)
|
|
1113
|
+
rec.handlers = collectHandlers(type, newProps);
|
|
1114
|
+
},
|
|
1115
|
+
commitTextUpdate() {},
|
|
1116
|
+
hideInstance(inst) {
|
|
1117
|
+
activeBatch.push({ op: "hide", id: inst.id });
|
|
1118
|
+
},
|
|
1119
|
+
unhideInstance(inst) {
|
|
1120
|
+
activeBatch.push({ op: "unhide", id: inst.id });
|
|
1121
|
+
},
|
|
1122
|
+
hideTextInstance() {},
|
|
1123
|
+
unhideTextInstance() {},
|
|
1124
|
+
getPublicInstance: (i) => i,
|
|
1125
|
+
detachDeletedInstance() {},
|
|
1126
|
+
maySuspendCommit: () => false,
|
|
1127
|
+
preloadInstance: () => true,
|
|
1128
|
+
startSuspendingCommit() {},
|
|
1129
|
+
suspendInstance() {},
|
|
1130
|
+
waitForCommitToBeReady: () => null,
|
|
1131
|
+
resolveUpdatePriority: () => currentUpdatePriority,
|
|
1132
|
+
getCurrentUpdatePriority: () => currentUpdatePriority,
|
|
1133
|
+
setCurrentUpdatePriority: (p) => {
|
|
1134
|
+
currentUpdatePriority = p;
|
|
1135
|
+
},
|
|
1136
|
+
shouldAttemptEagerTransition: () => false,
|
|
1137
|
+
trackSchedulerEvent() {},
|
|
1138
|
+
resolveEventType: () => null,
|
|
1139
|
+
resolveEventTimeStamp: () => -1.1,
|
|
1140
|
+
requestPostPaintCallback() {}
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
// src/system.ts
|
|
1144
|
+
function requireHost(name) {
|
|
1145
|
+
if (!getHmrState())
|
|
1146
|
+
throw new Error(`${name}() used before render(): no host connection yet`);
|
|
1147
|
+
}
|
|
1148
|
+
function call(method, params = {}) {
|
|
1149
|
+
requireHost(method);
|
|
1150
|
+
return getHmrState().ndp.request(method, params);
|
|
1151
|
+
}
|
|
1152
|
+
function registry2() {
|
|
1153
|
+
if (!globalThis.__nd_system_events)
|
|
1154
|
+
globalThis.__nd_system_events = new Map;
|
|
1155
|
+
return globalThis.__nd_system_events;
|
|
1156
|
+
}
|
|
1157
|
+
function subscribe(channel, name, handler) {
|
|
1158
|
+
requireHost(name);
|
|
1159
|
+
let handlers = registry2().get(channel);
|
|
1160
|
+
if (!handlers)
|
|
1161
|
+
registry2().set(channel, handlers = new Set);
|
|
1162
|
+
handlers.add(handler);
|
|
1163
|
+
return () => handlers.delete(handler);
|
|
1164
|
+
}
|
|
1165
|
+
function dispatchSystemEvent(channel, data) {
|
|
1166
|
+
if (channel === "app.activate")
|
|
1167
|
+
globalThis.__nd_app_active = true;
|
|
1168
|
+
else if (channel === "app.deactivate")
|
|
1169
|
+
globalThis.__nd_app_active = false;
|
|
1170
|
+
for (const handler of registry2().get(channel) ?? [])
|
|
1171
|
+
handler(data);
|
|
1172
|
+
if (channel === "notification.click") {
|
|
1173
|
+
const id = data?.id;
|
|
1174
|
+
if (id !== undefined)
|
|
1175
|
+
notificationData().delete(id);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
var NOTIFICATION_DATA_CAP = 128;
|
|
1179
|
+
function notificationData() {
|
|
1180
|
+
if (!globalThis.__nd_notification_data)
|
|
1181
|
+
globalThis.__nd_notification_data = new Map;
|
|
1182
|
+
return globalThis.__nd_notification_data;
|
|
1183
|
+
}
|
|
1184
|
+
var dialog = {
|
|
1185
|
+
openFile(options = {}) {
|
|
1186
|
+
return call("dialog.openFile", options);
|
|
1187
|
+
},
|
|
1188
|
+
saveFile(options = {}) {
|
|
1189
|
+
return call("dialog.saveFile", options);
|
|
1190
|
+
},
|
|
1191
|
+
showMessage(options) {
|
|
1192
|
+
return call("dialog.showMessage", options);
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
var clipboard = {
|
|
1196
|
+
readText() {
|
|
1197
|
+
return call("clipboard.readText");
|
|
1198
|
+
},
|
|
1199
|
+
async writeText(text) {
|
|
1200
|
+
await call("clipboard.writeText", { text });
|
|
1201
|
+
},
|
|
1202
|
+
readImage() {
|
|
1203
|
+
return call("clipboard.readImage");
|
|
1204
|
+
}
|
|
1205
|
+
};
|
|
1206
|
+
var notifications = {
|
|
1207
|
+
async show(options) {
|
|
1208
|
+
const { data, ...wire } = options;
|
|
1209
|
+
const id = await call("notification.show", wire);
|
|
1210
|
+
if (data !== undefined) {
|
|
1211
|
+
const map = notificationData();
|
|
1212
|
+
map.set(id, data);
|
|
1213
|
+
while (map.size > NOTIFICATION_DATA_CAP) {
|
|
1214
|
+
const oldest = map.keys().next().value;
|
|
1215
|
+
if (oldest === undefined)
|
|
1216
|
+
break;
|
|
1217
|
+
map.delete(oldest);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
return id;
|
|
1221
|
+
},
|
|
1222
|
+
onClick(handler) {
|
|
1223
|
+
return subscribe("notification.click", "notifications.onClick", (raw) => {
|
|
1224
|
+
const { id } = raw;
|
|
1225
|
+
const data = notificationData().get(id);
|
|
1226
|
+
handler(data === undefined ? { id } : { id, data });
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
};
|
|
1230
|
+
var recentDocuments = {
|
|
1231
|
+
async add(path) {
|
|
1232
|
+
await call("recent.add", { path });
|
|
1233
|
+
},
|
|
1234
|
+
async clear() {
|
|
1235
|
+
await call("recent.clear");
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
var credentials = {
|
|
1239
|
+
async set(service, account, secret) {
|
|
1240
|
+
await call("credentials.set", { service, account, secret });
|
|
1241
|
+
},
|
|
1242
|
+
get(service, account) {
|
|
1243
|
+
return call("credentials.get", { service, account });
|
|
1244
|
+
},
|
|
1245
|
+
async delete(service, account) {
|
|
1246
|
+
await call("credentials.delete", { service, account });
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
var app = {
|
|
1250
|
+
isActive() {
|
|
1251
|
+
return globalThis.__nd_app_active ?? true;
|
|
1252
|
+
},
|
|
1253
|
+
onActivate(handler) {
|
|
1254
|
+
return subscribe("app.activate", "app.onActivate", () => handler());
|
|
1255
|
+
},
|
|
1256
|
+
onDeactivate(handler) {
|
|
1257
|
+
return subscribe("app.deactivate", "app.onDeactivate", () => handler());
|
|
1258
|
+
},
|
|
1259
|
+
onOpenUrl(handler) {
|
|
1260
|
+
return subscribe("app.openUrl", "app.onOpenUrl", (data) => handler(data.url));
|
|
1261
|
+
},
|
|
1262
|
+
onOpenFile(handler) {
|
|
1263
|
+
return subscribe("app.openFile", "app.onOpenFile", (data) => handler(data.paths));
|
|
1264
|
+
},
|
|
1265
|
+
onFileDrop(handler) {
|
|
1266
|
+
return subscribe("fileDrop", "app.onFileDrop", (data) => handler(data));
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
var system = {
|
|
1270
|
+
getAppearance() {
|
|
1271
|
+
return call("system.getAppearance");
|
|
1272
|
+
},
|
|
1273
|
+
onAppearanceChange(handler) {
|
|
1274
|
+
return subscribe("appearance", "system.onAppearanceChange", (data) => handler(data));
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
var audio = {
|
|
1278
|
+
play(options) {
|
|
1279
|
+
return call("audio.play", options);
|
|
1280
|
+
},
|
|
1281
|
+
async pause(handle) {
|
|
1282
|
+
await call("audio.pause", { handle });
|
|
1283
|
+
},
|
|
1284
|
+
async resume(handle) {
|
|
1285
|
+
await call("audio.resume", { handle });
|
|
1286
|
+
},
|
|
1287
|
+
async stop(handle) {
|
|
1288
|
+
await call("audio.stop", { handle });
|
|
1289
|
+
},
|
|
1290
|
+
async seek(handle, positionMs) {
|
|
1291
|
+
await call("audio.seek", { handle, position: positionMs });
|
|
1292
|
+
},
|
|
1293
|
+
async setVolume(handle, volume) {
|
|
1294
|
+
await call("audio.setVolume", { handle, volume });
|
|
1295
|
+
},
|
|
1296
|
+
onState(handler) {
|
|
1297
|
+
return subscribe("audio.state", "audio.onState", (data) => handler(data));
|
|
1298
|
+
},
|
|
1299
|
+
onSpectrum(handler) {
|
|
1300
|
+
return subscribe("audio.spectrum", "audio.onSpectrum", (data) => handler(data));
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
// src/errors.ts
|
|
1305
|
+
var RATE_MAX = 20;
|
|
1306
|
+
var RATE_WINDOW_MS = 1e4;
|
|
1307
|
+
function state2() {
|
|
1308
|
+
if (!globalThis.__nd_errors) {
|
|
1309
|
+
globalThis.__nd_errors = {
|
|
1310
|
+
policy: {},
|
|
1311
|
+
handlers: new Set,
|
|
1312
|
+
stamps: [],
|
|
1313
|
+
suppressedLogged: false,
|
|
1314
|
+
hintShown: false,
|
|
1315
|
+
renderFatal: new WeakSet
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
return globalThis.__nd_errors;
|
|
1319
|
+
}
|
|
1320
|
+
function decide(kind, p) {
|
|
1321
|
+
switch (kind) {
|
|
1322
|
+
case "renderUncaught":
|
|
1323
|
+
return "fatal";
|
|
1324
|
+
case "renderCaught":
|
|
1325
|
+
case "renderRecoverable":
|
|
1326
|
+
return "report";
|
|
1327
|
+
case "uncaughtException":
|
|
1328
|
+
return p.uncaughtException ?? "fatal";
|
|
1329
|
+
case "unhandledRejection":
|
|
1330
|
+
return p.unhandledRejection ?? (process.env.ND_FATAL_REJECTIONS === "1" ? "fatal" : "report");
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
function onUnhandledError(handler) {
|
|
1334
|
+
const handlers = state2().handlers;
|
|
1335
|
+
handlers.add(handler);
|
|
1336
|
+
return () => handlers.delete(handler);
|
|
1337
|
+
}
|
|
1338
|
+
function setUnhandledErrorPolicy(p) {
|
|
1339
|
+
const s = state2();
|
|
1340
|
+
s.policy = { ...s.policy, ...p };
|
|
1341
|
+
}
|
|
1342
|
+
var KIND_LABEL = {
|
|
1343
|
+
unhandledRejection: "unhandled rejection",
|
|
1344
|
+
uncaughtException: "uncaught exception",
|
|
1345
|
+
renderUncaught: "render error",
|
|
1346
|
+
renderCaught: "render error (caught by boundary)",
|
|
1347
|
+
renderRecoverable: "render error (recoverable)"
|
|
1348
|
+
};
|
|
1349
|
+
function toError(raw) {
|
|
1350
|
+
return raw instanceof Error ? raw : new Error(String(raw));
|
|
1351
|
+
}
|
|
1352
|
+
function admitReport() {
|
|
1353
|
+
const s = state2();
|
|
1354
|
+
const now = Date.now();
|
|
1355
|
+
while (s.stamps.length && now - s.stamps[0] > RATE_WINDOW_MS)
|
|
1356
|
+
s.stamps.shift();
|
|
1357
|
+
if (s.stamps.length >= RATE_MAX) {
|
|
1358
|
+
if (!s.suppressedLogged) {
|
|
1359
|
+
s.suppressedLogged = true;
|
|
1360
|
+
console.error("[nd] further error reports suppressed (rate limit)");
|
|
1361
|
+
}
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
s.suppressedLogged = false;
|
|
1365
|
+
s.stamps.push(now);
|
|
1366
|
+
return true;
|
|
1367
|
+
}
|
|
1368
|
+
function report(raw, kind, fatal, componentStack) {
|
|
1369
|
+
const s = state2();
|
|
1370
|
+
const error = toError(raw);
|
|
1371
|
+
if (fatal || admitReport()) {
|
|
1372
|
+
if (s.policy.log !== false) {
|
|
1373
|
+
let line = `[nd] ${KIND_LABEL[kind]}: ${error.message}
|
|
1374
|
+
${error.stack ?? ""}`;
|
|
1375
|
+
if (!fatal && isHot() && !s.hintShown) {
|
|
1376
|
+
s.hintShown = true;
|
|
1377
|
+
line += `
|
|
1378
|
+
[nd] the app kept running. Register onUnhandledError() from @nativedesktop/react to handle these.`;
|
|
1379
|
+
}
|
|
1380
|
+
console.error(line);
|
|
1381
|
+
}
|
|
1382
|
+
getHmrState()?.ndp.sendRuntimeError(error.message, error.stack ?? "", fatal);
|
|
1383
|
+
}
|
|
1384
|
+
const context = { kind, fatal, raw };
|
|
1385
|
+
if (componentStack !== undefined)
|
|
1386
|
+
context.componentStack = componentStack;
|
|
1387
|
+
for (const handler of s.handlers) {
|
|
1388
|
+
try {
|
|
1389
|
+
handler(error, context);
|
|
1390
|
+
} catch (handlerErr) {
|
|
1391
|
+
console.error(`[nd] onUnhandledError handler threw: ${String(handlerErr)}`);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
function reportRenderError(raw, kind, componentStack) {
|
|
1396
|
+
if (kind === "renderUncaught") {
|
|
1397
|
+
if (typeof raw === "object" && raw !== null)
|
|
1398
|
+
state2().renderFatal.add(raw);
|
|
1399
|
+
report(raw, kind, true, componentStack);
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
report(raw, kind, false, componentStack);
|
|
1403
|
+
}
|
|
1404
|
+
function installErrorHandlers() {
|
|
1405
|
+
if (globalThis.__nd_errors_installed)
|
|
1406
|
+
return;
|
|
1407
|
+
globalThis.__nd_errors_installed = true;
|
|
1408
|
+
process.on("unhandledRejection", (reason) => {
|
|
1409
|
+
const fatal = decide("unhandledRejection", state2().policy) === "fatal";
|
|
1410
|
+
report(reason, "unhandledRejection", fatal);
|
|
1411
|
+
if (fatal)
|
|
1412
|
+
process.exit(1);
|
|
1413
|
+
});
|
|
1414
|
+
process.on("uncaughtException", (err) => {
|
|
1415
|
+
const s = state2();
|
|
1416
|
+
if (typeof err === "object" && err !== null && s.renderFatal.has(err))
|
|
1417
|
+
process.exit(1);
|
|
1418
|
+
const fatal = decide("uncaughtException", s.policy) === "fatal";
|
|
1419
|
+
report(err, "uncaughtException", fatal);
|
|
1420
|
+
if (fatal)
|
|
1421
|
+
process.exit(1);
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// src/renderer.ts
|
|
1426
|
+
async function render(element) {
|
|
1427
|
+
installErrorHandlers();
|
|
1428
|
+
let state3 = getHmrState();
|
|
1429
|
+
if (!state3) {
|
|
1430
|
+
const ndp = await Ndp.connect();
|
|
1431
|
+
ndp.onSystemEvent((channel, data) => dispatchSystemEvent(channel, data));
|
|
1432
|
+
await ndp.handshake({ name: "bun", version: Bun.version });
|
|
1433
|
+
setBackend(ndp.backend);
|
|
1434
|
+
setHostManifest(ndp.hostWidgets, ndp.hostCommands);
|
|
1435
|
+
const batch = new Batch;
|
|
1436
|
+
const registry3 = new NodeRegistry;
|
|
1437
|
+
bindCommitTargets(batch, registry3);
|
|
1438
|
+
let commitId = 0;
|
|
1439
|
+
const configWithFlush = {
|
|
1440
|
+
...hostConfig,
|
|
1441
|
+
resetAfterCommit() {
|
|
1442
|
+
const ops = batch.drain();
|
|
1443
|
+
if (ops.length)
|
|
1444
|
+
ndp.sendCommit({ commitId: commitId++, generation: currentGeneration(), ops });
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
ndp.onEvent((e) => {
|
|
1448
|
+
setPriorityFor(e.priority ?? "discrete");
|
|
1449
|
+
registry3.get(e.nodeId)?.handlers[e.name]?.(e.payload);
|
|
1450
|
+
});
|
|
1451
|
+
const Reconciler = ReconcilerFactory(configWithFlush);
|
|
1452
|
+
if (isHot())
|
|
1453
|
+
setupRefresh(Reconciler);
|
|
1454
|
+
const container = { rootId: null };
|
|
1455
|
+
const root = Reconciler.createContainer(container, ConcurrentRoot, null, false, null, "nd", (e, info) => {
|
|
1456
|
+
reportRenderError(e, "renderUncaught", info?.componentStack);
|
|
1457
|
+
throw e;
|
|
1458
|
+
}, (e, info) => {
|
|
1459
|
+
reportRenderError(e, "renderCaught", info?.componentStack);
|
|
1460
|
+
}, (e, info) => {
|
|
1461
|
+
reportRenderError(e, "renderRecoverable", info?.componentStack);
|
|
1462
|
+
}, null);
|
|
1463
|
+
state3 = { ndp, root, reconciler: Reconciler, bootCount: 0 };
|
|
1464
|
+
setHmrState(state3);
|
|
1465
|
+
}
|
|
1466
|
+
state3.bootCount += 1;
|
|
1467
|
+
if (state3.bootCount === 1) {
|
|
1468
|
+
if (isHot())
|
|
1469
|
+
registerRoot(element.type);
|
|
1470
|
+
state3.reconciler.updateContainer(element, state3.root, null, () => {});
|
|
1471
|
+
} else if (isHot()) {
|
|
1472
|
+
hotUpdateRoot(element.type);
|
|
1473
|
+
} else {
|
|
1474
|
+
state3.reconciler.updateContainer(element, state3.root, null, () => {});
|
|
1475
|
+
}
|
|
1476
|
+
if (state3.bootCount === 1)
|
|
1477
|
+
await new Promise(() => {});
|
|
1478
|
+
}
|
|
1479
|
+
function dispatchWidgetCommand(caller, node, command, arg) {
|
|
1480
|
+
const state3 = getHmrState();
|
|
1481
|
+
if (!state3)
|
|
1482
|
+
throw new Error(`${caller}() before render(): no NDP connection yet`);
|
|
1483
|
+
state3.ndp.sendWidgetCommand(node.id, command, arg ?? null);
|
|
1484
|
+
}
|
|
1485
|
+
function sendCommand(node, command, arg) {
|
|
1486
|
+
const allowed = widgetCommands[node.type] ?? [];
|
|
1487
|
+
if (!allowed.includes(command)) {
|
|
1488
|
+
throw new Error(`<${node.type}> does not accept command "${command}" (valid: ${allowed.join(", ") || "none"})`);
|
|
1489
|
+
}
|
|
1490
|
+
if (isHot() && !hasCommand(node.type, command)) {
|
|
1491
|
+
const key = `${node.type}.${command}`;
|
|
1492
|
+
if (!warnedHostUnknownCommand.has(key)) {
|
|
1493
|
+
warnedHostUnknownCommand.add(key);
|
|
1494
|
+
console.warn(`sendCommand: the connected host does not dispatch "${key}" — gate it with hasCommand("${node.type}", "${command}").`);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
dispatchWidgetCommand("sendCommand", node, command, arg);
|
|
1498
|
+
}
|
|
1499
|
+
var warnedHostUnknownCommand = new Set;
|
|
1500
|
+
function sendNativeCommand(node, command, arg) {
|
|
1501
|
+
if (!command)
|
|
1502
|
+
throw new Error("sendNativeCommand() requires a non-empty command");
|
|
1503
|
+
dispatchWidgetCommand("sendNativeCommand", node, command, arg);
|
|
1504
|
+
}
|
|
1505
|
+
var defaultPool = { rootId: null };
|
|
1506
|
+
function createPool() {
|
|
1507
|
+
return { rootId: null };
|
|
1508
|
+
}
|
|
1509
|
+
function createPortal(children, pool = defaultPool) {
|
|
1510
|
+
const state3 = getHmrState();
|
|
1511
|
+
if (!state3)
|
|
1512
|
+
throw new Error("createPortal() before render(): no reconciler yet");
|
|
1513
|
+
const reconciler = state3.reconciler;
|
|
1514
|
+
return reconciler.createPortal(children, pool, null);
|
|
1515
|
+
}
|
|
1516
|
+
function moveNode(node, toParent, before) {
|
|
1517
|
+
dispatchWidgetCommand("moveNode", node, "__ndReparent", { parent: toParent.id, before: before?.id ?? null });
|
|
1518
|
+
}
|
|
1519
|
+
// src/metrics.ts
|
|
1520
|
+
var APPLE = { xs: 4, sm: 8, md: 12, lg: 20, xl: 24 };
|
|
1521
|
+
var GNOME = { xs: 3, sm: 6, md: 12, lg: 18, xl: 24 };
|
|
1522
|
+
function appleLanguage() {
|
|
1523
|
+
const backend = Platform.backend;
|
|
1524
|
+
if (backend !== "unknown")
|
|
1525
|
+
return backend === "appkit";
|
|
1526
|
+
return Platform.os === "macos";
|
|
1527
|
+
}
|
|
1528
|
+
var Spacing = {
|
|
1529
|
+
get xs() {
|
|
1530
|
+
return (appleLanguage() ? APPLE : GNOME).xs;
|
|
1531
|
+
},
|
|
1532
|
+
get sm() {
|
|
1533
|
+
return (appleLanguage() ? APPLE : GNOME).sm;
|
|
1534
|
+
},
|
|
1535
|
+
get md() {
|
|
1536
|
+
return (appleLanguage() ? APPLE : GNOME).md;
|
|
1537
|
+
},
|
|
1538
|
+
get lg() {
|
|
1539
|
+
return (appleLanguage() ? APPLE : GNOME).lg;
|
|
1540
|
+
},
|
|
1541
|
+
get xl() {
|
|
1542
|
+
return (appleLanguage() ? APPLE : GNOME).xl;
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
var ContentMargin = appleLanguage() ? 20 : 12;
|
|
1546
|
+
onBackendKnown(() => {
|
|
1547
|
+
ContentMargin = appleLanguage() ? 20 : 12;
|
|
1548
|
+
});
|
|
1549
|
+
// src/paths.ts
|
|
1550
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
1551
|
+
import { homedir } from "node:os";
|
|
1552
|
+
import { dirname, resolve } from "node:path";
|
|
1553
|
+
function appName() {
|
|
1554
|
+
let dir = process.cwd();
|
|
1555
|
+
while (true) {
|
|
1556
|
+
const manifest = resolve(dir, "nd-app.json");
|
|
1557
|
+
if (existsSync(manifest)) {
|
|
1558
|
+
const name = JSON.parse(readFileSync(manifest, "utf8")).name;
|
|
1559
|
+
if (name)
|
|
1560
|
+
return name;
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1563
|
+
const parent = dirname(dir);
|
|
1564
|
+
if (parent === dir)
|
|
1565
|
+
break;
|
|
1566
|
+
dir = parent;
|
|
1567
|
+
}
|
|
1568
|
+
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), "package.json"), "utf8"));
|
|
1569
|
+
if (!pkg.name)
|
|
1570
|
+
throw new Error("nd: could not resolve app name from package.json");
|
|
1571
|
+
return pkg.name;
|
|
1572
|
+
}
|
|
1573
|
+
function getAppDataDir() {
|
|
1574
|
+
const name = appName();
|
|
1575
|
+
switch (process.platform) {
|
|
1576
|
+
case "darwin":
|
|
1577
|
+
return resolve(homedir(), "Library", "Application Support", name);
|
|
1578
|
+
case "win32":
|
|
1579
|
+
return resolve(process.env.APPDATA ?? homedir(), name);
|
|
1580
|
+
default:
|
|
1581
|
+
return resolve(process.env.XDG_DATA_HOME || resolve(homedir(), ".local", "share"), name);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
function ensureAppDataDir() {
|
|
1585
|
+
const dir = getAppDataDir();
|
|
1586
|
+
mkdirSync(dir, { recursive: true });
|
|
1587
|
+
return dir;
|
|
1588
|
+
}
|
|
1589
|
+
// src/dev-react.ts
|
|
1590
|
+
import * as ReactFirstEval from "react";
|
|
1591
|
+
function pinnedReact() {
|
|
1592
|
+
if (!globalThis.__nd_react)
|
|
1593
|
+
globalThis.__nd_react = ReactFirstEval;
|
|
1594
|
+
return globalThis.__nd_react;
|
|
1595
|
+
}
|
|
1596
|
+
var useState = (...a) => pinnedReact().useState(...a);
|
|
1597
|
+
var useEffect = (...a) => pinnedReact().useEffect(...a);
|
|
1598
|
+
var useLayoutEffect = (...a) => pinnedReact().useLayoutEffect(...a);
|
|
1599
|
+
var useMemo = (...a) => pinnedReact().useMemo(...a);
|
|
1600
|
+
var useCallback = (...a) => pinnedReact().useCallback(...a);
|
|
1601
|
+
var useRef = (...a) => pinnedReact().useRef(...a);
|
|
1602
|
+
var useContext = (...a) => pinnedReact().useContext(...a);
|
|
1603
|
+
var useReducer = (...a) => pinnedReact().useReducer(...a);
|
|
1604
|
+
var useTransition = (...a) => pinnedReact().useTransition(...a);
|
|
1605
|
+
var useDeferredValue = (...a) => pinnedReact().useDeferredValue(...a);
|
|
1606
|
+
var useSyncExternalStore = (...a) => pinnedReact().useSyncExternalStore(...a);
|
|
1607
|
+
var useId = (...a) => pinnedReact().useId(...a);
|
|
1608
|
+
var use = (a) => pinnedReact().use(a);
|
|
1609
|
+
var startTransition = (...a) => pinnedReact().startTransition(...a);
|
|
1610
|
+
var memo = (...a) => pinnedReact().memo(...a);
|
|
1611
|
+
var forwardRef = (...a) => pinnedReact().forwardRef(...a);
|
|
1612
|
+
var createContext = (...a) => pinnedReact().createContext(...a);
|
|
1613
|
+
var Suspense2 = ReactFirstEval.Suspense;
|
|
1614
|
+
var Fragment3 = ReactFirstEval.Fragment;
|
|
1615
|
+
|
|
1616
|
+
// src/native-component.ts
|
|
1617
|
+
import { createElement } from "react";
|
|
1618
|
+
function defineNativeComponent(options) {
|
|
1619
|
+
return forwardRef(function NativeComponent(componentProps, ref) {
|
|
1620
|
+
const { props, onNativeEvent, ...hostProps } = componentProps;
|
|
1621
|
+
const setRef = useCallback((instance) => {
|
|
1622
|
+
if (typeof ref === "function") {
|
|
1623
|
+
ref(instance ? makeRef(instance) : null);
|
|
1624
|
+
} else if (ref) {
|
|
1625
|
+
ref.current = instance ? makeRef(instance) : null;
|
|
1626
|
+
}
|
|
1627
|
+
}, [ref]);
|
|
1628
|
+
return createElement("nativeview", {
|
|
1629
|
+
...hostProps,
|
|
1630
|
+
viewKind: options.viewKind,
|
|
1631
|
+
props: JSON.stringify(props),
|
|
1632
|
+
onNativeEvent: onNativeEvent ? (event) => onNativeEvent({ name: event.nativeName ?? "", data: event.data }) : undefined,
|
|
1633
|
+
ref: setRef
|
|
1634
|
+
});
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
var refCache = new WeakMap;
|
|
1638
|
+
function makeRef(instance) {
|
|
1639
|
+
let wrapper = refCache.get(instance);
|
|
1640
|
+
if (!wrapper) {
|
|
1641
|
+
wrapper = {
|
|
1642
|
+
id: instance.id,
|
|
1643
|
+
type: "nativeview",
|
|
1644
|
+
send(command, arg) {
|
|
1645
|
+
sendNativeCommand({ id: instance.id, type: "nativeview" }, command, arg);
|
|
1646
|
+
}
|
|
1647
|
+
};
|
|
1648
|
+
refCache.set(instance, wrapper);
|
|
1649
|
+
}
|
|
1650
|
+
return wrapper;
|
|
1651
|
+
}
|
|
1652
|
+
// src/webview.ts
|
|
1653
|
+
var pending = new Map;
|
|
1654
|
+
var seq2 = 0;
|
|
1655
|
+
function executeJavaScript(node, code) {
|
|
1656
|
+
const id = `js${++seq2}`;
|
|
1657
|
+
return new Promise((resolve2, reject2) => {
|
|
1658
|
+
pending.set(id, { resolve: resolve2, reject: reject2 });
|
|
1659
|
+
sendCommand(node, "executeJavaScript", { id, code });
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
function onJavaScriptResult(e) {
|
|
1663
|
+
const result = e.data;
|
|
1664
|
+
const call2 = pending.get(result.id);
|
|
1665
|
+
if (!call2)
|
|
1666
|
+
return;
|
|
1667
|
+
pending.delete(result.id);
|
|
1668
|
+
if (result.ok)
|
|
1669
|
+
call2.resolve(result.value ?? "");
|
|
1670
|
+
else
|
|
1671
|
+
call2.reject(new Error(result.error ?? "executeJavaScript failed"));
|
|
1672
|
+
}
|
|
1673
|
+
// src/dialogs.ts
|
|
1674
|
+
var pending2 = new Map;
|
|
1675
|
+
function checkAvailable(node, reject2) {
|
|
1676
|
+
const existing = pending2.get(node.id);
|
|
1677
|
+
if (!existing)
|
|
1678
|
+
return true;
|
|
1679
|
+
reject2(new Error(`<window> already has a "${existing.kind}" dialog pending; only one modal dialog per window is allowed at a time`));
|
|
1680
|
+
return false;
|
|
1681
|
+
}
|
|
1682
|
+
function settle(node, kind, data) {
|
|
1683
|
+
const call2 = pending2.get(node.id);
|
|
1684
|
+
if (!call2 || call2.kind !== kind)
|
|
1685
|
+
return;
|
|
1686
|
+
pending2.delete(node.id);
|
|
1687
|
+
switch (call2.kind) {
|
|
1688
|
+
case "showAlert":
|
|
1689
|
+
call2.resolve(data);
|
|
1690
|
+
break;
|
|
1691
|
+
case "openFile":
|
|
1692
|
+
call2.resolve(data);
|
|
1693
|
+
break;
|
|
1694
|
+
case "saveFile":
|
|
1695
|
+
call2.resolve(data);
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
function showAlert(node, options) {
|
|
1700
|
+
return new Promise((resolve2, reject2) => {
|
|
1701
|
+
if (!checkAvailable(node, reject2))
|
|
1702
|
+
return;
|
|
1703
|
+
pending2.set(node.id, { kind: "showAlert", resolve: resolve2, reject: reject2 });
|
|
1704
|
+
sendCommand(node, "showAlert", options);
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
function openFile(node, options = {}) {
|
|
1708
|
+
return new Promise((resolve2, reject2) => {
|
|
1709
|
+
if (!checkAvailable(node, reject2))
|
|
1710
|
+
return;
|
|
1711
|
+
pending2.set(node.id, { kind: "openFile", resolve: resolve2, reject: reject2 });
|
|
1712
|
+
sendCommand(node, "openFile", options);
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
function saveFile(node, options = {}) {
|
|
1716
|
+
return new Promise((resolve2, reject2) => {
|
|
1717
|
+
if (!checkAvailable(node, reject2))
|
|
1718
|
+
return;
|
|
1719
|
+
pending2.set(node.id, { kind: "saveFile", resolve: resolve2, reject: reject2 });
|
|
1720
|
+
sendCommand(node, "saveFile", options);
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
function showAbout(node, options) {
|
|
1724
|
+
sendCommand(node, "showAbout", options);
|
|
1725
|
+
}
|
|
1726
|
+
function showTabOverview(node) {
|
|
1727
|
+
sendCommand(node, "showTabOverview", {});
|
|
1728
|
+
}
|
|
1729
|
+
function onAlertResult(node, e) {
|
|
1730
|
+
settle(node, "showAlert", e.data);
|
|
1731
|
+
}
|
|
1732
|
+
function onOpenFileResult(node, e) {
|
|
1733
|
+
settle(node, "openFile", e.data);
|
|
1734
|
+
}
|
|
1735
|
+
function onSaveFileResult(node, e) {
|
|
1736
|
+
settle(node, "saveFile", e.data);
|
|
1737
|
+
}
|
|
1738
|
+
// src/toast.ts
|
|
1739
|
+
var pending3 = new Map;
|
|
1740
|
+
var seq3 = 0;
|
|
1741
|
+
function showToast(node, options) {
|
|
1742
|
+
const id = `toast${++seq3}`;
|
|
1743
|
+
return new Promise((resolve2) => {
|
|
1744
|
+
pending3.set(id, { resolve: resolve2 });
|
|
1745
|
+
sendCommand(node, "showToast", { id, ...options });
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
function dismissToast(node, id) {
|
|
1749
|
+
sendCommand(node, "dismissToast", id ? { id } : {});
|
|
1750
|
+
}
|
|
1751
|
+
function onToastButtonClicked(e) {
|
|
1752
|
+
const { id } = e.data;
|
|
1753
|
+
const toast = pending3.get(id);
|
|
1754
|
+
if (!toast)
|
|
1755
|
+
return;
|
|
1756
|
+
pending3.delete(id);
|
|
1757
|
+
toast.resolve({ buttonClicked: true });
|
|
1758
|
+
}
|
|
1759
|
+
function onToastDismissed(e) {
|
|
1760
|
+
const { id } = e.data;
|
|
1761
|
+
const toast = pending3.get(id);
|
|
1762
|
+
if (!toast)
|
|
1763
|
+
return;
|
|
1764
|
+
pending3.delete(id);
|
|
1765
|
+
toast.resolve({ buttonClicked: false });
|
|
1766
|
+
}
|
|
1767
|
+
// src/shell.ts
|
|
1768
|
+
import { dirname as dirname2 } from "node:path";
|
|
1769
|
+
async function run(cmd) {
|
|
1770
|
+
const proc = Bun.spawn(cmd, { stdin: "ignore", stdout: "ignore", stderr: "ignore" });
|
|
1771
|
+
const code = await proc.exited;
|
|
1772
|
+
if (code !== 0)
|
|
1773
|
+
throw new Error(`${cmd.join(" ")} exited with code ${code}`);
|
|
1774
|
+
}
|
|
1775
|
+
async function openExternal(url) {
|
|
1776
|
+
await run(Platform.os === "macos" ? ["open", url] : ["xdg-open", url]);
|
|
1777
|
+
}
|
|
1778
|
+
async function openPath(path) {
|
|
1779
|
+
await run(Platform.os === "macos" ? ["open", path] : ["xdg-open", path]);
|
|
1780
|
+
}
|
|
1781
|
+
async function revealPath(path) {
|
|
1782
|
+
if (Platform.os === "macos") {
|
|
1783
|
+
await run(["open", "-R", path]);
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
try {
|
|
1787
|
+
await run([
|
|
1788
|
+
"dbus-send",
|
|
1789
|
+
"--session",
|
|
1790
|
+
"--dest=org.freedesktop.FileManager1",
|
|
1791
|
+
"--type=method_call",
|
|
1792
|
+
"/org/freedesktop/FileManager1",
|
|
1793
|
+
"org.freedesktop.FileManager1.ShowItems",
|
|
1794
|
+
`array:string:file://${path}`,
|
|
1795
|
+
"string:"
|
|
1796
|
+
]);
|
|
1797
|
+
} catch {
|
|
1798
|
+
await run(["xdg-open", dirname2(path)]);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
// src/hooks.ts
|
|
1802
|
+
function useMountEffect(effect) {
|
|
1803
|
+
useEffect(effect, []);
|
|
1804
|
+
}
|
|
1805
|
+
// src/store.ts
|
|
1806
|
+
import { mkdirSync as mkdirSync2, renameSync, writeFileSync } from "node:fs";
|
|
1807
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
1808
|
+
import { dirname as dirname3, join, resolve as resolve2 } from "node:path";
|
|
1809
|
+
class StoreImpl {
|
|
1810
|
+
name;
|
|
1811
|
+
path;
|
|
1812
|
+
corruptPath;
|
|
1813
|
+
loadError;
|
|
1814
|
+
#options;
|
|
1815
|
+
#value;
|
|
1816
|
+
#loaded = false;
|
|
1817
|
+
#loadPromise;
|
|
1818
|
+
#subscribers = new Set;
|
|
1819
|
+
#timer;
|
|
1820
|
+
#dirty = false;
|
|
1821
|
+
#pendingSnapshot;
|
|
1822
|
+
#writeChain = Promise.resolve();
|
|
1823
|
+
constructor(options, path) {
|
|
1824
|
+
this.#options = options;
|
|
1825
|
+
this.name = options.name;
|
|
1826
|
+
this.path = path;
|
|
1827
|
+
this.corruptPath = join(dirname3(path), `${options.name}.corrupt.json`);
|
|
1828
|
+
}
|
|
1829
|
+
load() {
|
|
1830
|
+
if (!this.#loadPromise)
|
|
1831
|
+
this.#loadPromise = this.#doLoad();
|
|
1832
|
+
return this.#loadPromise;
|
|
1833
|
+
}
|
|
1834
|
+
async#doLoad() {
|
|
1835
|
+
let raw;
|
|
1836
|
+
try {
|
|
1837
|
+
raw = await readFile(this.path, "utf8");
|
|
1838
|
+
} catch {}
|
|
1839
|
+
let value;
|
|
1840
|
+
if (raw !== undefined) {
|
|
1841
|
+
let parsed;
|
|
1842
|
+
let rejected;
|
|
1843
|
+
try {
|
|
1844
|
+
parsed = JSON.parse(raw);
|
|
1845
|
+
} catch (err) {
|
|
1846
|
+
rejected = new Error(`nd: store "${this.name}" file is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
1847
|
+
}
|
|
1848
|
+
if (!rejected) {
|
|
1849
|
+
const { data, fromVersion } = unwrapEnvelope(parsed);
|
|
1850
|
+
const migrate = this.#options.migrate;
|
|
1851
|
+
if (migrate) {
|
|
1852
|
+
let migrated;
|
|
1853
|
+
try {
|
|
1854
|
+
migrated = migrate(data, fromVersion);
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
migrated = null;
|
|
1857
|
+
rejected = new Error(`nd: store "${this.name}" migrate threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
1858
|
+
}
|
|
1859
|
+
if (migrated === null) {
|
|
1860
|
+
rejected ??= new Error(`nd: store "${this.name}" migrate rejected the file (version ${fromVersion})`);
|
|
1861
|
+
} else {
|
|
1862
|
+
value = migrated;
|
|
1863
|
+
}
|
|
1864
|
+
} else if (fromVersion === this.#options.version) {
|
|
1865
|
+
value = data;
|
|
1866
|
+
} else {
|
|
1867
|
+
rejected = new Error(`nd: store "${this.name}" is version ${fromVersion}, expected ${this.#options.version}, and no migrate hook is set`);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
if (value === undefined) {
|
|
1871
|
+
this.loadError = rejected;
|
|
1872
|
+
try {
|
|
1873
|
+
await rename(this.path, this.corruptPath);
|
|
1874
|
+
} catch {}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
this.#value = value ?? this.#options.defaults;
|
|
1878
|
+
this.#loaded = true;
|
|
1879
|
+
return this.#value;
|
|
1880
|
+
}
|
|
1881
|
+
get() {
|
|
1882
|
+
if (!this.#loaded)
|
|
1883
|
+
throw this.#beforeLoadError();
|
|
1884
|
+
return this.#value;
|
|
1885
|
+
}
|
|
1886
|
+
set(next) {
|
|
1887
|
+
if (!this.#loaded)
|
|
1888
|
+
throw this.#beforeLoadError();
|
|
1889
|
+
if (Object.is(next, this.#value))
|
|
1890
|
+
return;
|
|
1891
|
+
this.#value = next;
|
|
1892
|
+
for (const cb of this.#subscribers)
|
|
1893
|
+
cb(next);
|
|
1894
|
+
this.#scheduleWrite();
|
|
1895
|
+
}
|
|
1896
|
+
update(fn) {
|
|
1897
|
+
this.set(fn(this.get()));
|
|
1898
|
+
}
|
|
1899
|
+
subscribe(cb) {
|
|
1900
|
+
this.#subscribers.add(cb);
|
|
1901
|
+
return () => this.#subscribers.delete(cb);
|
|
1902
|
+
}
|
|
1903
|
+
flush() {
|
|
1904
|
+
if (this.#timer) {
|
|
1905
|
+
clearTimeout(this.#timer);
|
|
1906
|
+
this.#timer = undefined;
|
|
1907
|
+
}
|
|
1908
|
+
if (this.#dirty)
|
|
1909
|
+
this.#queueWrite();
|
|
1910
|
+
return this.#writeChain;
|
|
1911
|
+
}
|
|
1912
|
+
#beforeLoadError() {
|
|
1913
|
+
return new Error(`nd: store "${this.name}" read before load(); await store.load() before render()`);
|
|
1914
|
+
}
|
|
1915
|
+
#scheduleWrite() {
|
|
1916
|
+
this.#dirty = true;
|
|
1917
|
+
if (this.#timer)
|
|
1918
|
+
clearTimeout(this.#timer);
|
|
1919
|
+
this.#timer = setTimeout(() => {
|
|
1920
|
+
this.#timer = undefined;
|
|
1921
|
+
this.#queueWrite();
|
|
1922
|
+
}, this.#options.debounceMs ?? 250);
|
|
1923
|
+
}
|
|
1924
|
+
#queueWrite() {
|
|
1925
|
+
this.#dirty = false;
|
|
1926
|
+
const snapshot = this.#serialize();
|
|
1927
|
+
this.#pendingSnapshot = snapshot;
|
|
1928
|
+
this.#writeChain = this.#writeChain.then(async () => {
|
|
1929
|
+
await mkdir(dirname3(this.path), { recursive: true });
|
|
1930
|
+
const tmp = `${this.path}.tmp-${process.pid}`;
|
|
1931
|
+
await writeFile(tmp, snapshot);
|
|
1932
|
+
await rename(tmp, this.path);
|
|
1933
|
+
if (this.#pendingSnapshot === snapshot)
|
|
1934
|
+
this.#pendingSnapshot = undefined;
|
|
1935
|
+
}).catch((err) => {
|
|
1936
|
+
console.error(`[nd] store "${this.name}" write failed:`, err);
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
#serialize() {
|
|
1940
|
+
const envelope = { version: this.#options.version, data: this.#value };
|
|
1941
|
+
return JSON.stringify(envelope);
|
|
1942
|
+
}
|
|
1943
|
+
writeSyncPending() {
|
|
1944
|
+
if (!this.#loaded || !this.#dirty && this.#pendingSnapshot === undefined)
|
|
1945
|
+
return;
|
|
1946
|
+
this.#dirty = false;
|
|
1947
|
+
this.#pendingSnapshot = undefined;
|
|
1948
|
+
if (this.#timer) {
|
|
1949
|
+
clearTimeout(this.#timer);
|
|
1950
|
+
this.#timer = undefined;
|
|
1951
|
+
}
|
|
1952
|
+
try {
|
|
1953
|
+
mkdirSync2(dirname3(this.path), { recursive: true });
|
|
1954
|
+
const tmp = `${this.path}.tmp-exit-${process.pid}`;
|
|
1955
|
+
writeFileSync(tmp, this.#serialize());
|
|
1956
|
+
renameSync(tmp, this.path);
|
|
1957
|
+
} catch (err) {
|
|
1958
|
+
console.error(`[nd] store "${this.name}" exit flush failed:`, err);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
function unwrapEnvelope(parsed) {
|
|
1963
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.version === "number" && "data" in parsed) {
|
|
1964
|
+
return { data: parsed.data, fromVersion: parsed.version };
|
|
1965
|
+
}
|
|
1966
|
+
return { data: parsed, fromVersion: 0 };
|
|
1967
|
+
}
|
|
1968
|
+
function registry3() {
|
|
1969
|
+
if (!globalThis.__nd_stores)
|
|
1970
|
+
globalThis.__nd_stores = { stores: new Map, exitHooked: false };
|
|
1971
|
+
return globalThis.__nd_stores;
|
|
1972
|
+
}
|
|
1973
|
+
function hookExit(reg) {
|
|
1974
|
+
if (reg.exitHooked)
|
|
1975
|
+
return;
|
|
1976
|
+
reg.exitHooked = true;
|
|
1977
|
+
const flushAll = () => {
|
|
1978
|
+
for (const store of reg.stores.values())
|
|
1979
|
+
store.writeSyncPending();
|
|
1980
|
+
};
|
|
1981
|
+
process.once("exit", flushAll);
|
|
1982
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
1983
|
+
process.once(signal, () => {
|
|
1984
|
+
flushAll();
|
|
1985
|
+
if (process.listenerCount(signal) === 0)
|
|
1986
|
+
process.exit(signal === "SIGINT" ? 130 : 143);
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
function createStore(options) {
|
|
1991
|
+
const dir = options.dir ?? getAppDataDir();
|
|
1992
|
+
const path = resolve2(join(dir, `${options.name}.json`));
|
|
1993
|
+
const reg = registry3();
|
|
1994
|
+
const existing = reg.stores.get(path);
|
|
1995
|
+
if (existing)
|
|
1996
|
+
return existing;
|
|
1997
|
+
const store = new StoreImpl(options, path);
|
|
1998
|
+
reg.stores.set(path, store);
|
|
1999
|
+
hookExit(reg);
|
|
2000
|
+
return store;
|
|
2001
|
+
}
|
|
2002
|
+
function useStoreValue(store, select) {
|
|
2003
|
+
const getSnapshot = useMemo(() => {
|
|
2004
|
+
let lastValue;
|
|
2005
|
+
let lastSelected;
|
|
2006
|
+
let primed = false;
|
|
2007
|
+
return () => {
|
|
2008
|
+
const value = store.get();
|
|
2009
|
+
if (!primed || !Object.is(value, lastValue)) {
|
|
2010
|
+
lastValue = value;
|
|
2011
|
+
lastSelected = select ? select(value) : value;
|
|
2012
|
+
primed = true;
|
|
2013
|
+
}
|
|
2014
|
+
return lastSelected;
|
|
2015
|
+
};
|
|
2016
|
+
}, [store, select]);
|
|
2017
|
+
const subscribe2 = useMemo(() => {
|
|
2018
|
+
return (onChange) => store.subscribe(() => onChange());
|
|
2019
|
+
}, [store]);
|
|
2020
|
+
return useSyncExternalStore(subscribe2, getSnapshot);
|
|
2021
|
+
}
|
|
2022
|
+
export {
|
|
2023
|
+
useTransition,
|
|
2024
|
+
useSyncExternalStore,
|
|
2025
|
+
useStoreValue,
|
|
2026
|
+
useState,
|
|
2027
|
+
useRef,
|
|
2028
|
+
useReducer,
|
|
2029
|
+
useMountEffect,
|
|
2030
|
+
useMemo,
|
|
2031
|
+
useLayoutEffect,
|
|
2032
|
+
useId,
|
|
2033
|
+
useEffect,
|
|
2034
|
+
useDeferredValue,
|
|
2035
|
+
useContext,
|
|
2036
|
+
useCallback,
|
|
2037
|
+
use,
|
|
2038
|
+
system,
|
|
2039
|
+
startTransition,
|
|
2040
|
+
showToast,
|
|
2041
|
+
showTabOverview,
|
|
2042
|
+
showAlert,
|
|
2043
|
+
showAbout,
|
|
2044
|
+
setUnhandledErrorPolicy,
|
|
2045
|
+
sendNativeCommand,
|
|
2046
|
+
sendCommand,
|
|
2047
|
+
saveFile,
|
|
2048
|
+
revealPath,
|
|
2049
|
+
render,
|
|
2050
|
+
registerExports,
|
|
2051
|
+
recentDocuments,
|
|
2052
|
+
performRefresh,
|
|
2053
|
+
openPath,
|
|
2054
|
+
openFile,
|
|
2055
|
+
openExternal,
|
|
2056
|
+
onUnhandledError,
|
|
2057
|
+
onToastDismissed,
|
|
2058
|
+
onToastButtonClicked,
|
|
2059
|
+
onSaveFileResult,
|
|
2060
|
+
onOpenFileResult,
|
|
2061
|
+
onJavaScriptResult,
|
|
2062
|
+
onAlertResult,
|
|
2063
|
+
notifications,
|
|
2064
|
+
moveNode,
|
|
2065
|
+
memo,
|
|
2066
|
+
hasWidget,
|
|
2067
|
+
hasCommand,
|
|
2068
|
+
getAppDataDir,
|
|
2069
|
+
fullReload,
|
|
2070
|
+
forwardRef,
|
|
2071
|
+
executeJavaScript,
|
|
2072
|
+
ensureAppDataDir,
|
|
2073
|
+
dismissToast,
|
|
2074
|
+
dialog,
|
|
2075
|
+
defineNativeComponent,
|
|
2076
|
+
credentials,
|
|
2077
|
+
createStore,
|
|
2078
|
+
createPortal,
|
|
2079
|
+
createPool,
|
|
2080
|
+
createContext,
|
|
2081
|
+
clipboard,
|
|
2082
|
+
audio,
|
|
2083
|
+
app,
|
|
2084
|
+
Suspense2 as Suspense,
|
|
2085
|
+
Spacing,
|
|
2086
|
+
Platform,
|
|
2087
|
+
Fragment3 as Fragment,
|
|
2088
|
+
ContentMargin
|
|
2089
|
+
};
|