janela 0.3.1 → 0.5.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 +178 -7
- package/api/global.d.ts +27 -0
- package/api/index.d.ts +102 -0
- package/api/index.js +64 -0
- package/bin/janela.mjs +15 -1
- package/package.json +15 -1
- package/runtime/janela.ts +38 -118
- package/runtime/types.ts +251 -0
- package/templates/index.html +4 -2
- package/templates/main.ts +1 -1
- package/templates/react/deps.json +14 -2
- package/templates/react/files/index.html +1 -1
- package/templates/react/files/src/App.tsx +55 -0
- package/templates/react/files/src/main.tsx +12 -0
- package/templates/react/files/src-host/main.ts +48 -22
- package/templates/react/files/tsconfig.json +25 -0
- package/templates/solid/deps.json +11 -2
- package/templates/solid/files/index.html +1 -1
- package/templates/solid/files/src/App.tsx +55 -0
- package/templates/solid/files/src/main.tsx +7 -0
- package/templates/solid/files/src-host/main.ts +48 -22
- package/templates/solid/files/tsconfig.json +26 -0
- package/templates/svelte/deps.json +6 -1
- package/templates/svelte/files/index.html +1 -1
- package/templates/svelte/files/src/App.svelte +15 -7
- package/templates/svelte/files/src/main.ts +7 -0
- package/templates/svelte/files/src-host/main.ts +48 -22
- package/templates/svelte/files/tsconfig.json +23 -0
- package/templates/vue/deps.json +12 -2
- package/templates/vue/files/index.html +1 -1
- package/templates/vue/files/src/App.vue +16 -8
- package/templates/vue/files/src-host/main.ts +48 -22
- package/templates/vue/files/tsconfig.json +23 -0
- package/templates/react/files/src/App.jsx +0 -38
- package/templates/react/files/src/main.jsx +0 -9
- package/templates/solid/files/src/App.jsx +0 -35
- package/templates/solid/files/src/main.jsx +0 -4
- package/templates/svelte/files/src/main.js +0 -4
- /package/templates/vue/files/src/{main.js → main.ts} +0 -0
|
@@ -1,42 +1,68 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The contract below is the single declaration of what this app exposes. The
|
|
4
|
+
// frontend imports `App` with `import type`, so the page is checked against
|
|
5
|
+
// these exact types — command names, argument shapes, results and event
|
|
6
|
+
// payloads — with no code generation and nothing to keep in sync.
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// Two gotchas inherited from scriptc:
|
|
9
|
+
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
10
|
+
// wrap it in any expression (`+ 0`);
|
|
11
|
+
// - write `args: null` for a command that takes nothing, not `undefined` —
|
|
12
|
+
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
|
+
// to compile.
|
|
10
14
|
|
|
11
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
defineCommands,
|
|
17
|
+
defineEvents,
|
|
18
|
+
emit,
|
|
19
|
+
on,
|
|
20
|
+
onAsync,
|
|
21
|
+
type JanelaApp,
|
|
22
|
+
} from "janela/host";
|
|
23
|
+
|
|
24
|
+
export const commands = defineCommands<{
|
|
25
|
+
add: { args: { a: number; b: number }; result: number };
|
|
26
|
+
greet: { args: { name: string }; result: string };
|
|
27
|
+
log: { args: string; result: null };
|
|
28
|
+
wait: { args: { ms: number }; result: string };
|
|
29
|
+
quit: { args: null; result: null };
|
|
30
|
+
}>();
|
|
31
|
+
|
|
32
|
+
export const events = defineEvents<{
|
|
33
|
+
added: number;
|
|
34
|
+
}>();
|
|
35
|
+
|
|
36
|
+
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
+
export type App = { commands: typeof commands; events: typeof events };
|
|
12
38
|
|
|
13
39
|
export function setup(app: JanelaApp): void {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
40
|
+
// `args` is inferred from the contract, and the return type is checked
|
|
41
|
+
// against it — no casts, and no way to drift from what the page expects.
|
|
42
|
+
on(app, commands, "add", (args) => {
|
|
43
|
+
const sum = args.a + args.b;
|
|
44
|
+
emit(app, events, "added", sum);
|
|
19
45
|
return sum;
|
|
20
46
|
});
|
|
21
47
|
|
|
22
|
-
app
|
|
23
|
-
|
|
24
|
-
return "Hello, " + a.name + " — from the native TS binary";
|
|
48
|
+
on(app, commands, "greet", (args) => {
|
|
49
|
+
return "Hello, " + args.name + " — from the native TS binary";
|
|
25
50
|
});
|
|
26
51
|
|
|
27
|
-
app
|
|
28
|
-
console.log("[host] page says:", args
|
|
52
|
+
on(app, commands, "log", (args) => {
|
|
53
|
+
console.log("[host] page says:", args);
|
|
54
|
+
return null;
|
|
29
55
|
});
|
|
30
56
|
|
|
31
57
|
// An async command: answers later, without freezing the window.
|
|
32
|
-
app
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
resolve("waited " + a.ms + "ms without blocking the UI");
|
|
58
|
+
onAsync(app, commands, "wait", (args, resolve) => {
|
|
59
|
+
app.sleep(args.ms, () => {
|
|
60
|
+
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
36
61
|
});
|
|
37
62
|
});
|
|
38
63
|
|
|
39
|
-
app
|
|
64
|
+
on(app, commands, "quit", (_args) => {
|
|
40
65
|
app.quit();
|
|
66
|
+
return null;
|
|
41
67
|
});
|
|
42
68
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2022",
|
|
8
|
+
"DOM",
|
|
9
|
+
"DOM.Iterable"
|
|
10
|
+
],
|
|
11
|
+
"types": [],
|
|
12
|
+
"jsx": "preserve",
|
|
13
|
+
"jsxImportSource": "solid-js",
|
|
14
|
+
"strict": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"allowImportingTsExtensions": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"isolatedModules": true,
|
|
19
|
+
"verbatimModuleSyntax": true
|
|
20
|
+
},
|
|
21
|
+
"include": [
|
|
22
|
+
"src/**/*.ts",
|
|
23
|
+
"src/**/*.tsx",
|
|
24
|
+
"src-host/**/*.ts"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
"devDependencies": {
|
|
3
3
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
|
4
4
|
"svelte": "^5.16.0",
|
|
5
|
-
"vite": "^6.0.7"
|
|
5
|
+
"vite": "^6.0.7",
|
|
6
|
+
"svelte-check": "^4.1.4",
|
|
7
|
+
"typescript": "^5.7.3"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"typecheck": "svelte-check --tsconfig ./tsconfig.json"
|
|
6
11
|
}
|
|
7
12
|
}
|
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
<script>
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { createClient } from "janela/api";
|
|
3
|
+
// Type-only import of the host contract: erased at compile time, so no
|
|
4
|
+
// host code is bundled into the page.
|
|
5
|
+
import type { App as Contract } from "../src-host/main";
|
|
6
|
+
|
|
7
|
+
const client = createClient<Contract>();
|
|
8
|
+
|
|
2
9
|
let greeting = $state("…");
|
|
3
10
|
let a = $state(2);
|
|
4
11
|
let b = $state(40);
|
|
5
|
-
let sum = $state(null);
|
|
6
|
-
let events = $state([]);
|
|
12
|
+
let sum = $state<number | null>(null);
|
|
13
|
+
let events = $state<string[]>([]);
|
|
7
14
|
|
|
8
|
-
// Backend→frontend events. The payload arrives as a value
|
|
9
|
-
|
|
15
|
+
// Backend→frontend events. The payload arrives as a value, and the generic
|
|
16
|
+
// says which value.
|
|
17
|
+
client.on("added", (value) => (events = [`host emitted: ${value}`, ...events]));
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
client.invoke("greet", { name: "__NAME__" }).then((g) => (greeting = g));
|
|
12
20
|
|
|
13
21
|
async function add() {
|
|
14
|
-
sum = await
|
|
22
|
+
sum = await client.invoke("add", { a: Number(a), b: Number(b) });
|
|
15
23
|
}
|
|
16
24
|
</script>
|
|
17
25
|
|
|
@@ -1,42 +1,68 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The contract below is the single declaration of what this app exposes. The
|
|
4
|
+
// frontend imports `App` with `import type`, so the page is checked against
|
|
5
|
+
// these exact types — command names, argument shapes, results and event
|
|
6
|
+
// payloads — with no code generation and nothing to keep in sync.
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// Two gotchas inherited from scriptc:
|
|
9
|
+
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
10
|
+
// wrap it in any expression (`+ 0`);
|
|
11
|
+
// - write `args: null` for a command that takes nothing, not `undefined` —
|
|
12
|
+
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
|
+
// to compile.
|
|
10
14
|
|
|
11
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
defineCommands,
|
|
17
|
+
defineEvents,
|
|
18
|
+
emit,
|
|
19
|
+
on,
|
|
20
|
+
onAsync,
|
|
21
|
+
type JanelaApp,
|
|
22
|
+
} from "janela/host";
|
|
23
|
+
|
|
24
|
+
export const commands = defineCommands<{
|
|
25
|
+
add: { args: { a: number; b: number }; result: number };
|
|
26
|
+
greet: { args: { name: string }; result: string };
|
|
27
|
+
log: { args: string; result: null };
|
|
28
|
+
wait: { args: { ms: number }; result: string };
|
|
29
|
+
quit: { args: null; result: null };
|
|
30
|
+
}>();
|
|
31
|
+
|
|
32
|
+
export const events = defineEvents<{
|
|
33
|
+
added: number;
|
|
34
|
+
}>();
|
|
35
|
+
|
|
36
|
+
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
+
export type App = { commands: typeof commands; events: typeof events };
|
|
12
38
|
|
|
13
39
|
export function setup(app: JanelaApp): void {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
40
|
+
// `args` is inferred from the contract, and the return type is checked
|
|
41
|
+
// against it — no casts, and no way to drift from what the page expects.
|
|
42
|
+
on(app, commands, "add", (args) => {
|
|
43
|
+
const sum = args.a + args.b;
|
|
44
|
+
emit(app, events, "added", sum);
|
|
19
45
|
return sum;
|
|
20
46
|
});
|
|
21
47
|
|
|
22
|
-
app
|
|
23
|
-
|
|
24
|
-
return "Hello, " + a.name + " — from the native TS binary";
|
|
48
|
+
on(app, commands, "greet", (args) => {
|
|
49
|
+
return "Hello, " + args.name + " — from the native TS binary";
|
|
25
50
|
});
|
|
26
51
|
|
|
27
|
-
app
|
|
28
|
-
console.log("[host] page says:", args
|
|
52
|
+
on(app, commands, "log", (args) => {
|
|
53
|
+
console.log("[host] page says:", args);
|
|
54
|
+
return null;
|
|
29
55
|
});
|
|
30
56
|
|
|
31
57
|
// An async command: answers later, without freezing the window.
|
|
32
|
-
app
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
resolve("waited " + a.ms + "ms without blocking the UI");
|
|
58
|
+
onAsync(app, commands, "wait", (args, resolve) => {
|
|
59
|
+
app.sleep(args.ms, () => {
|
|
60
|
+
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
36
61
|
});
|
|
37
62
|
});
|
|
38
63
|
|
|
39
|
-
app
|
|
64
|
+
on(app, commands, "quit", (_args) => {
|
|
40
65
|
app.quit();
|
|
66
|
+
return null;
|
|
41
67
|
});
|
|
42
68
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2022",
|
|
8
|
+
"DOM",
|
|
9
|
+
"DOM.Iterable"
|
|
10
|
+
],
|
|
11
|
+
"types": [],
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"verbatimModuleSyntax": true
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"src/**/*.ts",
|
|
20
|
+
"src/**/*.svelte",
|
|
21
|
+
"src-host/**/*.ts"
|
|
22
|
+
]
|
|
23
|
+
}
|
package/templates/vue/deps.json
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"dependencies": {
|
|
3
|
-
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"vue": "^3.5.13"
|
|
4
|
+
},
|
|
5
|
+
"devDependencies": {
|
|
6
|
+
"@vitejs/plugin-vue": "^5.2.1",
|
|
7
|
+
"vite": "^6.0.7",
|
|
8
|
+
"typescript": "^5.7.3",
|
|
9
|
+
"vue-tsc": "^2.2.0"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"typecheck": "vue-tsc --noEmit"
|
|
13
|
+
}
|
|
4
14
|
}
|
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import { onMounted, ref } from "vue";
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, onUnmounted, ref } from "vue";
|
|
3
|
+
import { createClient } from "janela/api";
|
|
4
|
+
// A type-only import of the host's contract. It is erased at compile time, so
|
|
5
|
+
// no host code is bundled into the page — this is purely a type edge.
|
|
6
|
+
import type { App } from "../src-host/main";
|
|
7
|
+
|
|
8
|
+
const client = createClient<App>();
|
|
3
9
|
|
|
4
10
|
const greeting = ref("…");
|
|
5
11
|
const a = ref(2);
|
|
6
12
|
const b = ref(40);
|
|
7
|
-
const sum = ref(null);
|
|
8
|
-
const events = ref([]);
|
|
13
|
+
const sum = ref<number | null>(null);
|
|
14
|
+
const events = ref<string[]>([]);
|
|
9
15
|
|
|
10
|
-
//
|
|
11
|
-
|
|
16
|
+
// The event name is checked against the contract and `value` is inferred.
|
|
17
|
+
// `on` returns a disposer.
|
|
18
|
+
const off = client.on("added", (value) => events.value.unshift(`host emitted: ${value}`));
|
|
19
|
+
onUnmounted(off);
|
|
12
20
|
|
|
13
21
|
onMounted(async () => {
|
|
14
|
-
greeting.value = await
|
|
22
|
+
greeting.value = await client.invoke("greet", { name: "__NAME__" });
|
|
15
23
|
});
|
|
16
24
|
|
|
17
25
|
async function add() {
|
|
18
|
-
sum.value = await
|
|
26
|
+
sum.value = await client.invoke("add", { a: Number(a.value), b: Number(b.value) });
|
|
19
27
|
}
|
|
20
28
|
</script>
|
|
21
29
|
|
|
@@ -1,42 +1,68 @@
|
|
|
1
1
|
// src-host/main.ts — your app's backend, compiled to native code by scriptc.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The contract below is the single declaration of what this app exposes. The
|
|
4
|
+
// frontend imports `App` with `import type`, so the page is checked against
|
|
5
|
+
// these exact types — command names, argument shapes, results and event
|
|
6
|
+
// payloads — with no code generation and nothing to keep in sync.
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// Two gotchas inherited from scriptc:
|
|
9
|
+
// - never use a bare FFI-backed call as a complete variable initializer;
|
|
10
|
+
// wrap it in any expression (`+ 0`);
|
|
11
|
+
// - write `args: null` for a command that takes nothing, not `undefined` —
|
|
12
|
+
// an `undefined` argument lowers to a zero-parameter function and fails
|
|
13
|
+
// to compile.
|
|
10
14
|
|
|
11
|
-
import
|
|
15
|
+
import {
|
|
16
|
+
defineCommands,
|
|
17
|
+
defineEvents,
|
|
18
|
+
emit,
|
|
19
|
+
on,
|
|
20
|
+
onAsync,
|
|
21
|
+
type JanelaApp,
|
|
22
|
+
} from "janela/host";
|
|
23
|
+
|
|
24
|
+
export const commands = defineCommands<{
|
|
25
|
+
add: { args: { a: number; b: number }; result: number };
|
|
26
|
+
greet: { args: { name: string }; result: string };
|
|
27
|
+
log: { args: string; result: null };
|
|
28
|
+
wait: { args: { ms: number }; result: string };
|
|
29
|
+
quit: { args: null; result: null };
|
|
30
|
+
}>();
|
|
31
|
+
|
|
32
|
+
export const events = defineEvents<{
|
|
33
|
+
added: number;
|
|
34
|
+
}>();
|
|
35
|
+
|
|
36
|
+
/** The contract the page imports with `import type { App } from "../src-host/main"`. */
|
|
37
|
+
export type App = { commands: typeof commands; events: typeof events };
|
|
12
38
|
|
|
13
39
|
export function setup(app: JanelaApp): void {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
40
|
+
// `args` is inferred from the contract, and the return type is checked
|
|
41
|
+
// against it — no casts, and no way to drift from what the page expects.
|
|
42
|
+
on(app, commands, "add", (args) => {
|
|
43
|
+
const sum = args.a + args.b;
|
|
44
|
+
emit(app, events, "added", sum);
|
|
19
45
|
return sum;
|
|
20
46
|
});
|
|
21
47
|
|
|
22
|
-
app
|
|
23
|
-
|
|
24
|
-
return "Hello, " + a.name + " — from the native TS binary";
|
|
48
|
+
on(app, commands, "greet", (args) => {
|
|
49
|
+
return "Hello, " + args.name + " — from the native TS binary";
|
|
25
50
|
});
|
|
26
51
|
|
|
27
|
-
app
|
|
28
|
-
console.log("[host] page says:", args
|
|
52
|
+
on(app, commands, "log", (args) => {
|
|
53
|
+
console.log("[host] page says:", args);
|
|
54
|
+
return null;
|
|
29
55
|
});
|
|
30
56
|
|
|
31
57
|
// An async command: answers later, without freezing the window.
|
|
32
|
-
app
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
resolve("waited " + a.ms + "ms without blocking the UI");
|
|
58
|
+
onAsync(app, commands, "wait", (args, resolve) => {
|
|
59
|
+
app.sleep(args.ms, () => {
|
|
60
|
+
resolve("waited " + args.ms + "ms without blocking the UI");
|
|
36
61
|
});
|
|
37
62
|
});
|
|
38
63
|
|
|
39
|
-
app
|
|
64
|
+
on(app, commands, "quit", (_args) => {
|
|
40
65
|
app.quit();
|
|
66
|
+
return null;
|
|
41
67
|
});
|
|
42
68
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2022",
|
|
8
|
+
"DOM",
|
|
9
|
+
"DOM.Iterable"
|
|
10
|
+
],
|
|
11
|
+
"types": [],
|
|
12
|
+
"strict": true,
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"verbatimModuleSyntax": true
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"src/**/*.ts",
|
|
20
|
+
"src/**/*.vue",
|
|
21
|
+
"src-host/**/*.ts"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import "./App.css";
|
|
3
|
-
|
|
4
|
-
export default function App() {
|
|
5
|
-
const [greeting, setGreeting] = useState("…");
|
|
6
|
-
const [a, setA] = useState(2);
|
|
7
|
-
const [b, setB] = useState(40);
|
|
8
|
-
const [sum, setSum] = useState(null);
|
|
9
|
-
const [events, setEvents] = useState([]);
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
// Backend→frontend events. The payload arrives as a value.
|
|
13
|
-
janela.listen("added", (value) =>
|
|
14
|
-
setEvents((prev) => [`host emitted: ${value}`, ...prev]),
|
|
15
|
-
);
|
|
16
|
-
janela.invoke("greet", { name: "__NAME__" }).then(setGreeting);
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
|
-
const add = async () =>
|
|
20
|
-
setSum(await janela.invoke("add", { a: Number(a), b: Number(b) }));
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<>
|
|
24
|
-
<h1>{greeting}</h1>
|
|
25
|
-
<p>
|
|
26
|
-
<input type="number" value={a} onChange={(e) => setA(e.target.value)} /> +
|
|
27
|
-
<input type="number" value={b} onChange={(e) => setB(e.target.value)} />
|
|
28
|
-
<button onClick={add}>add</button>
|
|
29
|
-
{sum !== null && <span> = {sum}</span>}
|
|
30
|
-
</p>
|
|
31
|
-
<ul>
|
|
32
|
-
{events.map((e, i) => (
|
|
33
|
-
<li key={i}>{e}</li>
|
|
34
|
-
))}
|
|
35
|
-
</ul>
|
|
36
|
-
</>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { createSignal, onMount, For, Show } from "solid-js";
|
|
2
|
-
import "./App.css";
|
|
3
|
-
|
|
4
|
-
export default function App() {
|
|
5
|
-
const [greeting, setGreeting] = createSignal("…");
|
|
6
|
-
const [a, setA] = createSignal(2);
|
|
7
|
-
const [b, setB] = createSignal(40);
|
|
8
|
-
const [sum, setSum] = createSignal(null);
|
|
9
|
-
const [events, setEvents] = createSignal([]);
|
|
10
|
-
|
|
11
|
-
// Backend→frontend events. The payload arrives as a value.
|
|
12
|
-
janela.listen("added", (value) =>
|
|
13
|
-
setEvents((prev) => [`host emitted: ${value}`, ...prev]),
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
onMount(async () => setGreeting(await janela.invoke("greet", { name: "__NAME__" })));
|
|
17
|
-
|
|
18
|
-
const add = async () =>
|
|
19
|
-
setSum(await janela.invoke("add", { a: Number(a()), b: Number(b()) }));
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<>
|
|
23
|
-
<h1>{greeting()}</h1>
|
|
24
|
-
<p>
|
|
25
|
-
<input type="number" value={a()} onInput={(e) => setA(e.currentTarget.value)} /> +
|
|
26
|
-
<input type="number" value={b()} onInput={(e) => setB(e.currentTarget.value)} />
|
|
27
|
-
<button onClick={add}>add</button>
|
|
28
|
-
<Show when={sum() !== null}><span> = {sum()}</span></Show>
|
|
29
|
-
</p>
|
|
30
|
-
<ul>
|
|
31
|
-
<For each={events()}>{(e) => <li>{e}</li>}</For>
|
|
32
|
-
</ul>
|
|
33
|
-
</>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
File without changes
|