janela 0.5.0 → 0.7.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/README.md +87 -16
- package/api/index.d.ts +26 -6
- package/bin/janela.mjs +9 -1
- package/package.json +2 -2
- package/runtime/janela.ts +387 -266
- package/runtime/types.ts +15 -127
- package/templates/react/files/src-host/main.ts +23 -26
- package/templates/solid/files/src-host/main.ts +23 -26
- package/templates/svelte/files/src-host/main.ts +23 -26
- package/templates/vue/files/src-host/main.ts +23 -26
package/runtime/types.ts
CHANGED
|
@@ -73,64 +73,6 @@ export interface WindowConfig {
|
|
|
73
73
|
height: number;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export interface JanelaApp {
|
|
77
|
-
handle: number;
|
|
78
|
-
names: string[];
|
|
79
|
-
handlers: CommandHandler[];
|
|
80
|
-
/** Register a named command, callable from the page as janela.invoke(name, args). */
|
|
81
|
-
command: (name: string, h: CommandHandler) => void;
|
|
82
|
-
/** Register a command that answers later; see AsyncCommandHandler. */
|
|
83
|
-
commandAsync: (name: string, h: AsyncCommandHandler) => void;
|
|
84
|
-
/** Run fn on the next turn of the host loop — the way to slice long work. */
|
|
85
|
-
defer: (fn: () => void) => void;
|
|
86
|
-
/** Run fn after at least ms. The host loop's timer; scriptc's setTimeout
|
|
87
|
-
* cannot fire while the window is open (its loop is parked inside run()). */
|
|
88
|
-
sleep: (ms: number, fn: () => void) => void;
|
|
89
|
-
/**
|
|
90
|
-
* Read a file without blocking the window. The syscall runs on a shim
|
|
91
|
-
* worker thread; the callback lands on the UI thread on a later turn.
|
|
92
|
-
* Prefer this over node:fs readFileSync inside a command — that one blocks
|
|
93
|
-
* the loop, and with it the whole window.
|
|
94
|
-
*/
|
|
95
|
-
readFileAsync: (path: string, cb: FsCallback) => void;
|
|
96
|
-
/** Write a file without blocking the window; cb(null) on success. */
|
|
97
|
-
writeFileAsync: (
|
|
98
|
-
path: string,
|
|
99
|
-
data: string,
|
|
100
|
-
cb: (err: string | null) => void,
|
|
101
|
-
) => void;
|
|
102
|
-
/**
|
|
103
|
-
* Show the native "open" dialog. `cb` gets the chosen paths, or null if the
|
|
104
|
-
* user cancelled. The modal runs on a later turn of the UI thread, so
|
|
105
|
-
* calling this from inside a command does not block that command's reply —
|
|
106
|
-
* pair it with commandAsync when the page is waiting for the result.
|
|
107
|
-
*/
|
|
108
|
-
openFileDialog: (
|
|
109
|
-
options: OpenDialogOptions,
|
|
110
|
-
cb: (paths: string[] | null, err?: string) => void,
|
|
111
|
-
) => void;
|
|
112
|
-
/** Show the native "save" dialog; cb gets the path, or null on cancel. */
|
|
113
|
-
saveFileDialog: (
|
|
114
|
-
options: SaveDialogOptions,
|
|
115
|
-
cb: (path: string | null, err?: string) => void,
|
|
116
|
-
) => void;
|
|
117
|
-
/** Change the window title at any time, not just at startup. */
|
|
118
|
-
setTitle: (title: string) => void;
|
|
119
|
-
/**
|
|
120
|
-
* Resize the window. `hint` is webview's sizing hint: 0 none, 1 minimum,
|
|
121
|
-
* 2 maximum, 3 fixed.
|
|
122
|
-
*/
|
|
123
|
-
setSize: (width: number, height: number, hint?: number) => void;
|
|
124
|
-
/** Enter or leave fullscreen. */
|
|
125
|
-
setFullscreen: (on: boolean) => void;
|
|
126
|
-
/** Fire an event into the page; the payload is delivered as a value. */
|
|
127
|
-
emit: (event: string, payload: unknown) => void;
|
|
128
|
-
/** Close the window and make run() return. */
|
|
129
|
-
quit: () => void;
|
|
130
|
-
/** Show the page and block until the window closes. Returns the run status. */
|
|
131
|
-
run: (html: string) => number;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
76
|
// ---------------------------------------------------------------------------
|
|
135
77
|
// Typed IPC contract
|
|
136
78
|
// ---------------------------------------------------------------------------
|
|
@@ -143,11 +85,10 @@ export interface JanelaApp {
|
|
|
143
85
|
// Payloads still cross as JSON, so these types are compile-time only. Nothing
|
|
144
86
|
// validates a malformed payload at runtime.
|
|
145
87
|
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
// compile in a host build, while `on(app, commands, ...)` does.
|
|
88
|
+
// The contract is carried by the app's own type — `JanelaApp<Commands, Events>`
|
|
89
|
+
// — so the tables below are types and nothing more. The `Commands`/`Events`
|
|
90
|
+
// tokens and their `define*` constructors are the 0.5.x/0.6.x shape, kept so
|
|
91
|
+
// projects written against it still compile.
|
|
151
92
|
|
|
152
93
|
/** One command's argument and result types. */
|
|
153
94
|
export interface CommandShape {
|
|
@@ -174,78 +115,25 @@ export interface Events<E> {
|
|
|
174
115
|
/**
|
|
175
116
|
* Declare the commands a host exposes.
|
|
176
117
|
*
|
|
118
|
+
* @deprecated The contract needs no runtime token. Declare the tables as
|
|
119
|
+
* types and name the app itself:
|
|
120
|
+
*
|
|
177
121
|
* ```ts
|
|
178
|
-
* export
|
|
179
|
-
*
|
|
180
|
-
*
|
|
122
|
+
* export type AppCommands = { add: { args: { a: number; b: number }; result: number } };
|
|
123
|
+
* export type AppEvents = { added: number };
|
|
124
|
+
* export type App = JanelaApp<AppCommands, AppEvents>;
|
|
125
|
+
* export function setup(app: App): void { … }
|
|
181
126
|
* ```
|
|
182
127
|
*/
|
|
183
128
|
export function defineCommands<M extends CommandShapes>(): Commands<M> {
|
|
184
129
|
return {};
|
|
185
130
|
}
|
|
186
131
|
|
|
187
|
-
/** Declare the events a host emits: `defineEvents<{ added: number }>()`. */
|
|
188
|
-
export function defineEvents<E>(): Events<E> {
|
|
189
|
-
return {};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Register a command against the contract. `args` is inferred from the
|
|
194
|
-
* contract, and the return type is checked against it, so the handler is
|
|
195
|
-
* written once with no casts.
|
|
196
|
-
*
|
|
197
|
-
* ```ts
|
|
198
|
-
* on(app, commands, "add", (args) => args.a + args.b);
|
|
199
|
-
* ```
|
|
200
|
-
*/
|
|
201
|
-
export function on<M extends CommandShapes, K extends keyof M & string>(
|
|
202
|
-
app: JanelaApp,
|
|
203
|
-
_commands: Commands<M>,
|
|
204
|
-
name: K,
|
|
205
|
-
handler: (args: M[K]["args"]) => M[K]["result"],
|
|
206
|
-
): void {
|
|
207
|
-
app.command(name, (args: unknown) => handler(args as M[K]["args"]));
|
|
208
|
-
}
|
|
209
|
-
|
|
210
132
|
/**
|
|
211
|
-
*
|
|
212
|
-
* contract's result type; see AsyncCommandHandler for the timing rules.
|
|
213
|
-
*/
|
|
214
|
-
export function onAsync<M extends CommandShapes, K extends keyof M & string>(
|
|
215
|
-
app: JanelaApp,
|
|
216
|
-
_commands: Commands<M>,
|
|
217
|
-
name: K,
|
|
218
|
-
handler: (
|
|
219
|
-
args: M[K]["args"],
|
|
220
|
-
resolve: (value: M[K]["result"]) => void,
|
|
221
|
-
reject: (reason: unknown) => void,
|
|
222
|
-
) => void,
|
|
223
|
-
): void {
|
|
224
|
-
app.commandAsync(
|
|
225
|
-
name,
|
|
226
|
-
(args: unknown, resolve: (value: unknown) => void, reject: (reason: unknown) => void) => {
|
|
227
|
-
handler(
|
|
228
|
-
args as M[K]["args"],
|
|
229
|
-
(value: M[K]["result"]) => resolve(value),
|
|
230
|
-
reject,
|
|
231
|
-
);
|
|
232
|
-
},
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Emit a declared event. The name must exist in the contract and the payload
|
|
238
|
-
* must match its type.
|
|
133
|
+
* Declare the events a host emits: `defineEvents<{ added: number }>()`.
|
|
239
134
|
*
|
|
240
|
-
*
|
|
241
|
-
* emit(app, events, "added", 42);
|
|
242
|
-
* ```
|
|
135
|
+
* @deprecated Pass the event table to `JanelaApp` instead — see defineCommands.
|
|
243
136
|
*/
|
|
244
|
-
export function
|
|
245
|
-
|
|
246
|
-
_events: Events<E>,
|
|
247
|
-
name: K,
|
|
248
|
-
payload: E[K],
|
|
249
|
-
): void {
|
|
250
|
-
app.emit(name, payload);
|
|
137
|
+
export function defineEvents<E>(): Events<E> {
|
|
138
|
+
return {};
|
|
251
139
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// The two tables below are the single declaration of what this app exposes,
|
|
4
|
+
// and `App` names an app that carries them. The frontend imports `App` with
|
|
5
|
+
// `import type`, so the page is checked against these exact types — command
|
|
6
|
+
// names, argument shapes, results and event payloads — with no code
|
|
7
|
+
// generation and nothing to keep in sync.
|
|
7
8
|
//
|
|
8
9
|
// Two gotchas inherited from scriptc:
|
|
9
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
@@ -12,56 +13,52 @@
|
|
|
12
13
|
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
14
|
// to compile.
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
-
defineCommands,
|
|
17
|
-
defineEvents,
|
|
18
|
-
emit,
|
|
19
|
-
on,
|
|
20
|
-
onAsync,
|
|
21
|
-
type JanelaApp,
|
|
22
|
-
} from "janela/host";
|
|
16
|
+
import type { JanelaApp } from "janela/host";
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
|
+
export type AppCommands = {
|
|
25
20
|
add: { args: { a: number; b: number }; result: number };
|
|
26
21
|
greet: { args: { name: string }; result: string };
|
|
27
22
|
log: { args: string; result: null };
|
|
28
23
|
wait: { args: { ms: number }; result: string };
|
|
29
24
|
quit: { args: null; result: null };
|
|
30
|
-
}
|
|
25
|
+
};
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
/** Every event this app emits, and what each one carries. */
|
|
28
|
+
export type AppEvents = {
|
|
33
29
|
added: number;
|
|
34
|
-
}
|
|
30
|
+
};
|
|
35
31
|
|
|
36
32
|
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
-
export type App =
|
|
33
|
+
export type App = JanelaApp<AppCommands, AppEvents>;
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
// Typing the app with the contract is what makes `app.command` checked: the
|
|
36
|
+
// name must be one of the declared ones, `args` is inferred from it, and the
|
|
37
|
+
// return value has to match. Same for `app.emit`.
|
|
38
|
+
export function setup(app: App): void {
|
|
39
|
+
app.command("add", (args) => {
|
|
43
40
|
const sum = args.a + args.b;
|
|
44
|
-
emit(
|
|
41
|
+
app.emit("added", sum);
|
|
45
42
|
return sum;
|
|
46
43
|
});
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
app.command("greet", (args) => {
|
|
49
46
|
return "Hello, " + args.name + " — from the native TS binary";
|
|
50
47
|
});
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
app.command("log", (args) => {
|
|
53
50
|
console.log("[host] page says:", args);
|
|
54
51
|
return null;
|
|
55
52
|
});
|
|
56
53
|
|
|
57
54
|
// An async command: answers later, without freezing the window.
|
|
58
|
-
|
|
55
|
+
app.commandAsync("wait", (args, resolve) => {
|
|
59
56
|
app.sleep(args.ms, () => {
|
|
60
57
|
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
61
58
|
});
|
|
62
59
|
});
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
app.command("quit", (_args) => {
|
|
65
62
|
app.quit();
|
|
66
63
|
return null;
|
|
67
64
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// The two tables below are the single declaration of what this app exposes,
|
|
4
|
+
// and `App` names an app that carries them. The frontend imports `App` with
|
|
5
|
+
// `import type`, so the page is checked against these exact types — command
|
|
6
|
+
// names, argument shapes, results and event payloads — with no code
|
|
7
|
+
// generation and nothing to keep in sync.
|
|
7
8
|
//
|
|
8
9
|
// Two gotchas inherited from scriptc:
|
|
9
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
@@ -12,56 +13,52 @@
|
|
|
12
13
|
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
14
|
// to compile.
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
-
defineCommands,
|
|
17
|
-
defineEvents,
|
|
18
|
-
emit,
|
|
19
|
-
on,
|
|
20
|
-
onAsync,
|
|
21
|
-
type JanelaApp,
|
|
22
|
-
} from "janela/host";
|
|
16
|
+
import type { JanelaApp } from "janela/host";
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
|
+
export type AppCommands = {
|
|
25
20
|
add: { args: { a: number; b: number }; result: number };
|
|
26
21
|
greet: { args: { name: string }; result: string };
|
|
27
22
|
log: { args: string; result: null };
|
|
28
23
|
wait: { args: { ms: number }; result: string };
|
|
29
24
|
quit: { args: null; result: null };
|
|
30
|
-
}
|
|
25
|
+
};
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
/** Every event this app emits, and what each one carries. */
|
|
28
|
+
export type AppEvents = {
|
|
33
29
|
added: number;
|
|
34
|
-
}
|
|
30
|
+
};
|
|
35
31
|
|
|
36
32
|
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
-
export type App =
|
|
33
|
+
export type App = JanelaApp<AppCommands, AppEvents>;
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
// Typing the app with the contract is what makes `app.command` checked: the
|
|
36
|
+
// name must be one of the declared ones, `args` is inferred from it, and the
|
|
37
|
+
// return value has to match. Same for `app.emit`.
|
|
38
|
+
export function setup(app: App): void {
|
|
39
|
+
app.command("add", (args) => {
|
|
43
40
|
const sum = args.a + args.b;
|
|
44
|
-
emit(
|
|
41
|
+
app.emit("added", sum);
|
|
45
42
|
return sum;
|
|
46
43
|
});
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
app.command("greet", (args) => {
|
|
49
46
|
return "Hello, " + args.name + " — from the native TS binary";
|
|
50
47
|
});
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
app.command("log", (args) => {
|
|
53
50
|
console.log("[host] page says:", args);
|
|
54
51
|
return null;
|
|
55
52
|
});
|
|
56
53
|
|
|
57
54
|
// An async command: answers later, without freezing the window.
|
|
58
|
-
|
|
55
|
+
app.commandAsync("wait", (args, resolve) => {
|
|
59
56
|
app.sleep(args.ms, () => {
|
|
60
57
|
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
61
58
|
});
|
|
62
59
|
});
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
app.command("quit", (_args) => {
|
|
65
62
|
app.quit();
|
|
66
63
|
return null;
|
|
67
64
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// The two tables below are the single declaration of what this app exposes,
|
|
4
|
+
// and `App` names an app that carries them. The frontend imports `App` with
|
|
5
|
+
// `import type`, so the page is checked against these exact types — command
|
|
6
|
+
// names, argument shapes, results and event payloads — with no code
|
|
7
|
+
// generation and nothing to keep in sync.
|
|
7
8
|
//
|
|
8
9
|
// Two gotchas inherited from scriptc:
|
|
9
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
@@ -12,56 +13,52 @@
|
|
|
12
13
|
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
14
|
// to compile.
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
-
defineCommands,
|
|
17
|
-
defineEvents,
|
|
18
|
-
emit,
|
|
19
|
-
on,
|
|
20
|
-
onAsync,
|
|
21
|
-
type JanelaApp,
|
|
22
|
-
} from "janela/host";
|
|
16
|
+
import type { JanelaApp } from "janela/host";
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
|
+
export type AppCommands = {
|
|
25
20
|
add: { args: { a: number; b: number }; result: number };
|
|
26
21
|
greet: { args: { name: string }; result: string };
|
|
27
22
|
log: { args: string; result: null };
|
|
28
23
|
wait: { args: { ms: number }; result: string };
|
|
29
24
|
quit: { args: null; result: null };
|
|
30
|
-
}
|
|
25
|
+
};
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
/** Every event this app emits, and what each one carries. */
|
|
28
|
+
export type AppEvents = {
|
|
33
29
|
added: number;
|
|
34
|
-
}
|
|
30
|
+
};
|
|
35
31
|
|
|
36
32
|
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
-
export type App =
|
|
33
|
+
export type App = JanelaApp<AppCommands, AppEvents>;
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
// Typing the app with the contract is what makes `app.command` checked: the
|
|
36
|
+
// name must be one of the declared ones, `args` is inferred from it, and the
|
|
37
|
+
// return value has to match. Same for `app.emit`.
|
|
38
|
+
export function setup(app: App): void {
|
|
39
|
+
app.command("add", (args) => {
|
|
43
40
|
const sum = args.a + args.b;
|
|
44
|
-
emit(
|
|
41
|
+
app.emit("added", sum);
|
|
45
42
|
return sum;
|
|
46
43
|
});
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
app.command("greet", (args) => {
|
|
49
46
|
return "Hello, " + args.name + " — from the native TS binary";
|
|
50
47
|
});
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
app.command("log", (args) => {
|
|
53
50
|
console.log("[host] page says:", args);
|
|
54
51
|
return null;
|
|
55
52
|
});
|
|
56
53
|
|
|
57
54
|
// An async command: answers later, without freezing the window.
|
|
58
|
-
|
|
55
|
+
app.commandAsync("wait", (args, resolve) => {
|
|
59
56
|
app.sleep(args.ms, () => {
|
|
60
57
|
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
61
58
|
});
|
|
62
59
|
});
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
app.command("quit", (_args) => {
|
|
65
62
|
app.quit();
|
|
66
63
|
return null;
|
|
67
64
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// The two tables below are the single declaration of what this app exposes,
|
|
4
|
+
// and `App` names an app that carries them. The frontend imports `App` with
|
|
5
|
+
// `import type`, so the page is checked against these exact types — command
|
|
6
|
+
// names, argument shapes, results and event payloads — with no code
|
|
7
|
+
// generation and nothing to keep in sync.
|
|
7
8
|
//
|
|
8
9
|
// Two gotchas inherited from scriptc:
|
|
9
10
|
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
@@ -12,56 +13,52 @@
|
|
|
12
13
|
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
14
|
// to compile.
|
|
14
15
|
|
|
15
|
-
import {
|
|
16
|
-
defineCommands,
|
|
17
|
-
defineEvents,
|
|
18
|
-
emit,
|
|
19
|
-
on,
|
|
20
|
-
onAsync,
|
|
21
|
-
type JanelaApp,
|
|
22
|
-
} from "janela/host";
|
|
16
|
+
import type { JanelaApp } from "janela/host";
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
/** Every command this app answers. Declared once; the page checks against it. */
|
|
19
|
+
export type AppCommands = {
|
|
25
20
|
add: { args: { a: number; b: number }; result: number };
|
|
26
21
|
greet: { args: { name: string }; result: string };
|
|
27
22
|
log: { args: string; result: null };
|
|
28
23
|
wait: { args: { ms: number }; result: string };
|
|
29
24
|
quit: { args: null; result: null };
|
|
30
|
-
}
|
|
25
|
+
};
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
/** Every event this app emits, and what each one carries. */
|
|
28
|
+
export type AppEvents = {
|
|
33
29
|
added: number;
|
|
34
|
-
}
|
|
30
|
+
};
|
|
35
31
|
|
|
36
32
|
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
-
export type App =
|
|
33
|
+
export type App = JanelaApp<AppCommands, AppEvents>;
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
// Typing the app with the contract is what makes `app.command` checked: the
|
|
36
|
+
// name must be one of the declared ones, `args` is inferred from it, and the
|
|
37
|
+
// return value has to match. Same for `app.emit`.
|
|
38
|
+
export function setup(app: App): void {
|
|
39
|
+
app.command("add", (args) => {
|
|
43
40
|
const sum = args.a + args.b;
|
|
44
|
-
emit(
|
|
41
|
+
app.emit("added", sum);
|
|
45
42
|
return sum;
|
|
46
43
|
});
|
|
47
44
|
|
|
48
|
-
|
|
45
|
+
app.command("greet", (args) => {
|
|
49
46
|
return "Hello, " + args.name + " — from the native TS binary";
|
|
50
47
|
});
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
app.command("log", (args) => {
|
|
53
50
|
console.log("[host] page says:", args);
|
|
54
51
|
return null;
|
|
55
52
|
});
|
|
56
53
|
|
|
57
54
|
// An async command: answers later, without freezing the window.
|
|
58
|
-
|
|
55
|
+
app.commandAsync("wait", (args, resolve) => {
|
|
59
56
|
app.sleep(args.ms, () => {
|
|
60
57
|
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
61
58
|
});
|
|
62
59
|
});
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
app.command("quit", (_args) => {
|
|
65
62
|
app.quit();
|
|
66
63
|
return null;
|
|
67
64
|
});
|