src
applets
layouts
pages
scripts
styles
···
19
19
line-height: 100px;
20
20
text-align: center;
21
21
}
22
22
+
23
23
+
button {
24
24
+
line-height: 1.5;
25
25
+
}
22
26
</style>
23
27
24
28
<script>
···
10
10
11
11
<title>{title}</title>
12
12
13
13
-
<style>
13
13
+
<style is:global>
14
14
@import "../styles/reset.css";
15
15
@import "../styles/fonts.css";
16
16
@import "../styles/variables.css";
···
8
8
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
9
9
10
10
<title>{title}</title>
11
11
+
12
12
+
<style is:global>
13
13
+
@import "../styles/reset.css";
14
14
+
@import "../styles/fonts.css";
15
15
+
@import "../styles/variables.css";
16
16
+
</style>
11
17
</head>
12
18
<body>
13
19
<slot />
···
2
2
import Page from "../layouts/page.astro";
3
3
4
4
// import "@picocss/pico/css/pico.colors.css";
5
5
-
import "../styles/reset.css";
6
6
-
import "../styles/fonts.css";
7
5
import "../styles/pages/index.css";
8
6
9
7
const engines = [{ url: "engine/audio/", title: "Audio" }];
···
2
2
import Page from "../../../layouts/page.astro";
3
3
---
4
4
5
5
-
<Page title="Diffuse Applets Usage Example">
5
5
+
<Page title="Diffuse">
6
6
+
<script src="../../../scripts/themes/pilot/index.js"></script>
7
7
+
6
8
<!-- Theme applets -->
7
9
<iframe id="#applet__ui__audio" src="ui/audio/" frameborder="0" style="width: 100%"></iframe>
8
10
9
11
<!-- Other applets -->
10
12
<iframe id="#applet__engine__audio" src="../../engine/audio/" frameborder="0" height="0" width="0"
11
13
></iframe>
12
12
-
13
13
-
<div>Testing</div>
14
14
</Page>
15
15
-
16
16
-
<style is:global>
17
17
-
@import "../../../styles/reset.css";
18
18
-
@import "../../../styles/fonts.css";
19
19
-
@import "../../../styles/variables.css";
20
20
-
@import "../../../styles/themes/pilot/variables.css";
21
21
-
</style>
22
22
-
23
23
-
<style>
24
24
-
/***********************************
25
25
-
* Fonts
26
26
-
***********************************/
27
27
-
:root {
28
28
-
font-family: "Inter", sans-serif;
29
29
-
font-size: var(--fs-base);
30
30
-
}
31
31
-
32
32
-
@supports (font-variation-settings: normal) {
33
33
-
:root {
34
34
-
font-family: "InterVariable", sans-serif;
35
35
-
font-feature-settings:
36
36
-
"ss03" 2,
37
37
-
"ss02" 2;
38
38
-
font-optical-sizing: auto;
39
39
-
}
40
40
-
}
41
41
-
42
42
-
body {
43
43
-
background-color: var(--delicate-cloud);
44
44
-
color: var(--made-in-the-shade);
45
45
-
overflow: hidden;
46
46
-
}
47
47
-
48
48
-
/***********************************
49
49
-
* Applets | Position
50
50
-
***********************************/
51
51
-
#applet__ui__audio {
52
52
-
}
53
53
-
54
54
-
/* Position engines outside the viewframe (no UI for these) */
55
55
-
#applet__engine__audio {
56
56
-
left: 110vw;
57
57
-
position: absolute;
58
58
-
top: 110vh;
59
59
-
}
60
60
-
</style>
61
61
-
62
62
-
<script>
63
63
-
import { type Applet, type AppletEvent, applets } from "@web-applets/sdk";
64
64
-
import { effect, signal } from "spellcaster/spellcaster.js";
65
65
-
66
66
-
////////////////////////////////////////////
67
67
-
// 🗂️ Applets
68
68
-
////////////////////////////////////////////
69
69
-
const engine = {
70
70
-
audio: await applet("../../engine/audio"),
71
71
-
};
72
72
-
73
73
-
const ui = {
74
74
-
audio: await applet("ui/audio", { setHeight: true }),
75
75
-
};
76
76
-
77
77
-
////////////////////////////////////////////
78
78
-
// ⚡ Connect applets
79
79
-
////////////////////////////////////////////
80
80
-
reactive(ui.audio, (isPlaying: boolean) =>
81
81
-
engine.audio.sendAction(isPlaying ? "play" : "pause", null),
82
82
-
);
83
83
-
84
84
-
reactive(engine.audio, (isPlaying: boolean) => ui.audio.sendAction("set_is_playing", isPlaying));
85
85
-
reactive(engine.audio, (progress: number) => ui.audio.sendAction("set_progress", progress));
86
86
-
87
87
-
////////////////////////////////////////////
88
88
-
// 🪟 Applet initialiser
89
89
-
////////////////////////////////////////////
90
90
-
async function applet(src: string, opts: { setHeight?: boolean } = {}) {
91
91
-
const frame: HTMLIFrameElement = document.querySelector(
92
92
-
`[src="${src}${src.endsWith("/") ? "" : "/"}"]`,
93
93
-
);
94
94
-
95
95
-
const applet = await applets.connect(frame.contentWindow);
96
96
-
if (opts.setHeight) applet.onresize = () => (frame.height = `${applet.height}px`);
97
97
-
98
98
-
return applet;
99
99
-
}
100
100
-
101
101
-
////////////////////////////////////////////
102
102
-
// 🔮 Reactive state management
103
103
-
////////////////////////////////////////////
104
104
-
// TODO: Applet should have a subtype
105
105
-
function reactive<T>(applet: Applet, effectFn: (t: T) => void) {
106
106
-
const property = effectFn
107
107
-
.toString()
108
108
-
.slice(1)
109
109
-
.match(/([^(\)|,|\s)]+)/)[1];
110
110
-
111
111
-
const [getter, setter] = signal(applet.data[property] as T);
112
112
-
113
113
-
effect(() => effectFn(getter()));
114
114
-
115
115
-
applet.addEventListener("data", (event: AppletEvent) => {
116
116
-
setter(event.data[property]);
117
117
-
});
118
118
-
}
119
119
-
</script>
···
1
1
+
import { type Applet, type AppletEvent, applets } from "@web-applets/sdk";
2
2
+
import { effect, signal } from "spellcaster/spellcaster.js";
3
3
+
4
4
+
import "../../../styles/themes/pilot/index.css";
5
5
+
6
6
+
////////////////////////////////////////////
7
7
+
// 🗂️ Applets
8
8
+
////////////////////////////////////////////
9
9
+
const engine = {
10
10
+
audio: await applet("../../engine/audio"),
11
11
+
};
12
12
+
13
13
+
const ui = {
14
14
+
audio: await applet("ui/audio", { setHeight: true }),
15
15
+
};
16
16
+
17
17
+
////////////////////////////////////////////
18
18
+
// ⚡ Connect applets
19
19
+
////////////////////////////////////////////
20
20
+
reactive(
21
21
+
ui.audio,
22
22
+
(isPlaying: boolean) =>
23
23
+
engine.audio.sendAction(isPlaying ? "play" : "pause", null),
24
24
+
);
25
25
+
26
26
+
reactive(
27
27
+
engine.audio,
28
28
+
(isPlaying: boolean) => ui.audio.sendAction("set_is_playing", isPlaying),
29
29
+
);
30
30
+
31
31
+
reactive(
32
32
+
engine.audio,
33
33
+
(progress: number) => ui.audio.sendAction("set_progress", progress),
34
34
+
);
35
35
+
36
36
+
////////////////////////////////////////////
37
37
+
// 🪟 Applet initialiser
38
38
+
////////////////////////////////////////////
39
39
+
async function applet(src: string, opts: { setHeight?: boolean } = {}) {
40
40
+
const frame: HTMLIFrameElement | null = document.querySelector(
41
41
+
`[src="${src}${src.endsWith("/") ? "" : "/"}"]`,
42
42
+
);
43
43
+
44
44
+
if (frame === null) throw new Error("iframe element not found, src: " + src);
45
45
+
if (frame.contentWindow === null) {
46
46
+
throw new Error("iframe does not have a contentWindow");
47
47
+
}
48
48
+
49
49
+
const applet = await applets.connect(frame.contentWindow);
50
50
+
if (opts.setHeight) {
51
51
+
applet.onresize = () => (frame.height = `${applet.height}px`);
52
52
+
}
53
53
+
54
54
+
return applet;
55
55
+
}
56
56
+
57
57
+
////////////////////////////////////////////
58
58
+
// 🔮 Reactive state management
59
59
+
////////////////////////////////////////////
60
60
+
// TODO: Applet should have a subtype
61
61
+
function reactive<T>(applet: Applet, effectFn: (t: T) => void) {
62
62
+
const property = effectFn
63
63
+
.toString()
64
64
+
.slice(1)
65
65
+
.match(/([^(\)|,|\s)]+)/);
66
66
+
67
67
+
const [getter, setter] = signal(
68
68
+
(applet.data as any)[property ? property[1] : "ignore"] as T,
69
69
+
);
70
70
+
71
71
+
effect(() => effectFn(getter()));
72
72
+
73
73
+
applet.addEventListener("data", (event: AppletEvent) => {
74
74
+
setter(event.data[property ? property[1] : "ignore"]);
75
75
+
});
76
76
+
}
···
36
36
main {
37
37
display: flex;
38
38
flex-wrap: wrap;
39
39
-
gap: 0 var(--space-xl);
39
39
+
gap: 0 var(--space-3xl);
40
40
}
41
41
42
42
main > section {
43
43
-
flex: 1;
43
43
+
/* flex: 1; */
44
44
min-width: min(var(--container-xs), 100%);
45
45
-
width: 50%;
45
45
+
width: 32.5%;
46
46
}
47
47
48
48
a {
···
58
58
color: var(--beyond-the-pines);
59
59
font-size: var(--fs-xl);
60
60
font-weight: 900;
61
61
-
margin: var(--space-xl) 0 var(--space-sm);
61
61
+
line-height: 1;
62
62
+
margin: var(--space-2xl) 0 var(--space-md);
62
63
text-transform: uppercase;
63
64
}
64
65
65
66
h3 {
66
66
-
font-size: var(--fs-lg);
67
67
+
font-size: var(--fs-md);
67
68
font-weight: 800;
68
68
-
margin: var(--space-lg) 0 var(--space-sm);
69
69
+
line-height: 1;
70
70
+
margin: var(--space-xl) 0 var(--space-sm);
69
71
text-transform: uppercase;
72
72
+
}
73
73
+
74
74
+
h2 + h3 {
75
75
+
margin-top: var(--space-md);
70
76
}
71
77
72
78
ul,
···
1
1
+
@import "./variables.css";
2
2
+
3
3
+
/***********************************
4
4
+
* Fonts
5
5
+
***********************************/
6
6
+
:root {
7
7
+
font-family: "Inter", sans-serif;
8
8
+
font-size: var(--fs-base);
9
9
+
}
10
10
+
11
11
+
@supports (font-variation-settings: normal) {
12
12
+
:root {
13
13
+
font-family: "InterVariable", sans-serif;
14
14
+
font-feature-settings:
15
15
+
"ss03" 2,
16
16
+
"ss02" 2;
17
17
+
font-optical-sizing: auto;
18
18
+
}
19
19
+
}
20
20
+
21
21
+
body {
22
22
+
background-color: var(--delicate-cloud);
23
23
+
color: var(--made-in-the-shade);
24
24
+
overflow: hidden;
25
25
+
}
26
26
+
27
27
+
/***********************************
28
28
+
* Applets | Position
29
29
+
***********************************/
30
30
+
#applet__ui__audio {
31
31
+
}
32
32
+
33
33
+
/* Position engines outside the viewframe (no UI for these) */
34
34
+
#applet__engine__audio {
35
35
+
left: 110vw;
36
36
+
position: absolute;
37
37
+
top: 110vh;
38
38
+
}