alpha
Login
or
Join now
tokono.ma
/
diffuse-applets
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Experiment to rebuild Diffuse using web applets.
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
feat: Basics for storage configurator
author
Steven Vandevelde
date
1 year ago
(Apr 23, 2025, 6:36 PM +0200)
commit
88d316fa
88d316faaacb326d4a3614328b67b55c296e1d36
parent
3e1e1e6a
3e1e1e6a9f85e42e04b7333fba3727bc236b3d68
+375
-132
17 changed files
Expand all
Collapse all
Unified
Split
astro.config.js
deno.lock
package-lock.json
package.json
src
applets
configurator
storage
output
applet.astro
manifest.json
orchestrator
queue
applet.astro
manifest.json
storage
applet.astro
manifest.json
storage
output
memory
applet.astro
manifest.json
themes
pilot
ui
audio
applet.astro
pages
index.astro
scripts
themes
pilot
index.ts
styles
configurator.css
icons.css
+6
astro.config.js
Reviewed
···
1
1
+
import { defineConfig } from "astro/config";
2
2
+
import scope from "astro-scope";
3
3
+
4
4
+
export default defineConfig({
5
5
+
integrations: [scope()],
6
6
+
});
+3
deno.lock
Reviewed
···
22
22
"packageJson": {
23
23
"dependencies": [
24
24
"npm:@picocss/pico@^2.1.1",
25
25
+
"npm:astro-scope@^3.0.1",
25
26
"npm:astro@^5.7.4",
27
27
+
"npm:iconoir@^7.11.0",
28
28
+
"npm:idb-keyval@^6.2.1",
26
29
"npm:spellcaster@^5.0.2",
27
30
"npm:throttle-debounce@^5.0.2",
28
31
"npm:xxh32@^2.0.5"
+28
-1
package-lock.json
Reviewed
···
7
7
"dependencies": {
8
8
"@picocss/pico": "^2.1.1",
9
9
"@web-applets/sdk": "file:../../unternet-co/web-applets/sdk/",
10
10
+
"iconoir": "^7.11.0",
11
11
+
"idb-keyval": "^6.2.1",
10
12
"spellcaster": "^5.0.2",
11
13
"throttle-debounce": "^5.0.2",
12
14
"xxh32": "^2.0.5"
13
15
},
14
16
"devDependencies": {
15
15
-
"astro": "^5.7.4"
17
17
+
"astro": "^5.7.4",
18
18
+
"astro-scope": "^3.0.1"
16
19
}
17
20
},
18
21
"../../unternet-co/web-applets/sdk": {
22
22
+
"name": "@web-applets/sdk",
19
23
"version": "0.2.6",
20
24
"license": "MIT",
21
25
"devDependencies": {
···
1587
1591
"sharp": "^0.33.3"
1588
1592
}
1589
1593
},
1594
1594
+
"node_modules/astro-scope": {
1595
1595
+
"version": "3.0.1",
1596
1596
+
"resolved": "https://registry.npmjs.org/astro-scope/-/astro-scope-3.0.1.tgz",
1597
1597
+
"integrity": "sha512-/mdiiv0BELoDvhHPMCBokTymW11KOp+zTh4OMqmYGb8DxpOxE59O2VhtiWseoZdKJNQkNyyN/9Nsu3VZrPGWHA==",
1598
1598
+
"dev": true,
1599
1599
+
"peerDependencies": {
1600
1600
+
"astro": "^4.2.2 || 5"
1601
1601
+
}
1602
1602
+
},
1590
1603
"node_modules/axobject-query": {
1591
1604
"version": "4.1.0",
1592
1605
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
···
2454
2467
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
2455
2468
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
2456
2469
"dev": true
2470
2470
+
},
2471
2471
+
"node_modules/iconoir": {
2472
2472
+
"version": "7.11.0",
2473
2473
+
"resolved": "https://registry.npmjs.org/iconoir/-/iconoir-7.11.0.tgz",
2474
2474
+
"integrity": "sha512-F9T/E08aJBaQ+VOBjn+ChWKn3hFwsaK5VZ024OFMxdDaxKjLGDpU/OsU7MO9wXM+mDs4ZImypdXIn0fFZAXKmA==",
2475
2475
+
"funding": {
2476
2476
+
"type": "opencollective",
2477
2477
+
"url": "https://opencollective.com/iconoir"
2478
2478
+
}
2479
2479
+
},
2480
2480
+
"node_modules/idb-keyval": {
2481
2481
+
"version": "6.2.1",
2482
2482
+
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz",
2483
2483
+
"integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="
2457
2484
},
2458
2485
"node_modules/import-meta-resolve": {
2459
2486
"version": "4.1.0",
+4
-1
package.json
Reviewed
···
2
2
"dependencies": {
3
3
"@picocss/pico": "^2.1.1",
4
4
"@web-applets/sdk": "file:../../unternet-co/web-applets/sdk/",
5
5
+
"iconoir": "^7.11.0",
6
6
+
"idb-keyval": "^6.2.1",
5
7
"spellcaster": "^5.0.2",
6
8
"throttle-debounce": "^5.0.2",
7
9
"xxh32": "^2.0.5"
8
10
},
9
11
"devDependencies": {
10
10
-
"astro": "^5.7.4"
12
12
+
"astro": "^5.7.4",
13
13
+
"astro-scope": "^3.0.1"
11
14
}
12
15
}
+119
src/applets/configurator/storage/output/applet.astro
Reviewed
···
1
1
+
<main class="container">
2
2
+
<h1>Storage configuration</h1>
3
3
+
<p>Here you can select one or more storages (applets) to keep your user data on.</p>
4
4
+
<div id="options">
5
5
+
<p>
6
6
+
<span class="with-icon">
7
7
+
<i class="iconoir-app-window"></i>
8
8
+
<strong>Browser storage</strong>
9
9
+
</span>
10
10
+
11
11
+
<br />
12
12
+
13
13
+
<span class="pico-color-jade-500 with-icon">
14
14
+
<i class="iconoir-check-circle-solid"></i>
15
15
+
Activated
16
16
+
</span>
17
17
+
</p>
18
18
+
<p>
19
19
+
<span class="with-icon">
20
20
+
<i class="iconoir-laptop"></i>
21
21
+
<strong>Device storage</strong>
22
22
+
</span>
23
23
+
24
24
+
<br />
25
25
+
26
26
+
<span class="pico-color-amber-500 with-icon">
27
27
+
<i class="iconoir-settings"></i>
28
28
+
Needs configuration
29
29
+
</span>
30
30
+
</p>
31
31
+
<p>
32
32
+
<span class="with-icon">
33
33
+
<i class="iconoir-cloud"></i>
34
34
+
<strong>Dropbox</strong>
35
35
+
</span>
36
36
+
37
37
+
<br />
38
38
+
39
39
+
<span class="pico-color-grey-500 with-icon">
40
40
+
<i class="iconoir-xmark-circle"></i>
41
41
+
Deactivated
42
42
+
</span>
43
43
+
</p>
44
44
+
</div>
45
45
+
</main>
46
46
+
47
47
+
<style>
48
48
+
@import "../../../../styles/configurator.css";
49
49
+
50
50
+
.with-icon {
51
51
+
align-items: center;
52
52
+
display: inline-flex;
53
53
+
gap: 6px;
54
54
+
}
55
55
+
</style>
56
56
+
57
57
+
<script>
58
58
+
import * as IDB from "idb-keyval";
59
59
+
import { applets } from "@web-applets/sdk";
60
60
+
61
61
+
type Getter = ({ name }: { name: string }) => Promise<Uint8Array | undefined>;
62
62
+
type Setter = ({ data, name }: { data: Uint8Array; name: string }) => Promise<void>;
63
63
+
64
64
+
////////////////////////////////////////////
65
65
+
// SETUP
66
66
+
////////////////////////////////////////////
67
67
+
const context = applets.register();
68
68
+
69
69
+
// TODO: Should migrate + merge data when switching storages
70
70
+
71
71
+
// Initial state
72
72
+
context.data = undefined;
73
73
+
74
74
+
////////////////////////////////////////////
75
75
+
// ACTIONS
76
76
+
////////////////////////////////////////////
77
77
+
context.setActionHandler("get", get);
78
78
+
context.setActionHandler("put", put);
79
79
+
80
80
+
function selectedStorage() {
81
81
+
return localStorage.getItem("storage") || "indexeddb";
82
82
+
}
83
83
+
84
84
+
async function get(args: Parameters<Getter>[0]) {
85
85
+
let data;
86
86
+
87
87
+
switch (selectedStorage()) {
88
88
+
case "indexeddb": {
89
89
+
data = await idbGet(args);
90
90
+
break;
91
91
+
}
92
92
+
}
93
93
+
94
94
+
if (data) {
95
95
+
context.data = data;
96
96
+
} else {
97
97
+
context.data = undefined;
98
98
+
}
99
99
+
}
100
100
+
101
101
+
async function put(args: Parameters<Setter>[0]) {
102
102
+
switch (selectedStorage()) {
103
103
+
case "indexeddb":
104
104
+
return await idbPut(args);
105
105
+
}
106
106
+
}
107
107
+
108
108
+
////////////////////////////////////////////
109
109
+
// [Storages]
110
110
+
// INDEXED DB
111
111
+
////////////////////////////////////////////
112
112
+
const idbGet: Getter = async ({ name }) => {
113
113
+
return await IDB.get(name);
114
114
+
};
115
115
+
116
116
+
const idbPut: Setter = async ({ data, name }) => {
117
117
+
return await IDB.set(name, data);
118
118
+
};
119
119
+
</script>
+28
src/applets/configurator/storage/output/manifest.json
Reviewed
···
1
1
+
{
2
2
+
"name": "diffuse/configurator/storage/output",
3
3
+
"title": "Diffuse Configurator | Storage | Output",
4
4
+
"entrypoint": "index.html",
5
5
+
"actions": {
6
6
+
"get": {
7
7
+
"title": "Get",
8
8
+
"description": "Get data from the configured storage",
9
9
+
"params_schema": {
10
10
+
"type": "object",
11
11
+
"properties": {
12
12
+
"name": { "type": "string" }
13
13
+
}
14
14
+
}
15
15
+
},
16
16
+
"put": {
17
17
+
"title": "Put",
18
18
+
"description": "Put data on the configured storage",
19
19
+
"params_schema": {
20
20
+
"type": "object",
21
21
+
"properties": {
22
22
+
"data": { "type": "object" },
23
23
+
"name": { "type": "string" }
24
24
+
}
25
25
+
}
26
26
+
}
27
27
+
}
28
28
+
}
+19
-13
src/applets/orchestrator/queue/applet.astro
Reviewed
···
1
1
<script>
2
2
import { applets } from "@web-applets/sdk";
3
3
+
4
4
+
import type { Track, Output } from "../../core/types.d.ts";
3
5
import { applet, comparable, reactive } from "../../../scripts/theme";
4
6
5
7
////////////////////////////////////////////
6
8
// SETUP
7
9
////////////////////////////////////////////
8
8
-
import type { Track, Output } from "../../core/types.d.ts";
9
9
-
10
10
import type * as AudioEngine from "../../engine/audio/types.d.ts";
11
11
import type * as QueueEngine from "../../engine/queue/types.d.ts";
12
12
···
19
19
queue: await applet<QueueEngine.State>("../../engine/queue", { context: self.parent }),
20
20
};
21
21
22
22
-
const storage = {
23
23
-
output: {
24
24
-
memory: await applet<Output>("../../storage/output/memory", { context: self.parent }),
25
25
-
},
22
22
+
const orchestrator = {
23
23
+
storage: await applet<Output>("../../orchestrator/storage", { context: self.parent }),
26
24
};
27
25
28
26
////////////////////////////////////////////
···
35
33
// into a usable audio URL.
36
34
engine.queue.sendAction(
37
35
"add",
38
38
-
storage.output.memory.data.tracks.map((track: Track) => {
36
36
+
orchestrator.storage.data.tracks.map((track: Track) => {
39
37
return {
40
38
expiresAt: Infinity,
41
39
id: track.id,
···
57
55
engine.audio,
58
56
(data) => data.items[engine.queue.data.now?.id ?? Infinity]?.hasEnded ?? false,
59
57
(hasEnded) => {
60
60
-
if (hasEnded) engine.queue.sendAction("shift", null);
58
58
+
if (hasEnded) engine.queue.sendAction("shift");
61
59
},
62
60
);
63
61
···
99
97
);
100
98
101
99
////////////////////////////////////////////
102
102
-
// ⚙️ [Connections → Engines]
103
103
-
// 📦 Storage
100
100
+
// 🎻 [Connections → Orchestrators]
101
101
+
// 📦 STORAGE
104
102
////////////////////////////////////////////
105
103
reactive(
106
106
-
storage.output.memory,
107
107
-
(data) => comparable(data.tracks),
108
108
-
() => fill(),
104
104
+
orchestrator.storage,
105
105
+
(data) => (data ? comparable(data.tracks) : undefined),
106
106
+
(hash) => {
107
107
+
if (hash) fill();
108
108
+
},
109
109
);
110
110
</script>
111
111
+
112
112
+
<style>
113
113
+
iframe {
114
114
+
display: none;
115
115
+
}
116
116
+
</style>
+6
-1
src/applets/orchestrator/queue/manifest.json
Reviewed
···
2
2
"name": "diffuse/orchestrator/queue",
3
3
"title": "Diffuse Orchestrator | Queue",
4
4
"entrypoint": "index.html",
5
5
-
"actions": {}
5
5
+
"actions": {
6
6
+
"fill": {
7
7
+
"title": "Fill",
8
8
+
"description": "Fill up the queue."
9
9
+
}
10
10
+
}
6
11
}
+119
src/applets/orchestrator/storage/applet.astro
Reviewed
···
1
1
+
<script>
2
2
+
import { applets } from "@web-applets/sdk";
3
3
+
4
4
+
import type { Output, Source, Track } from "../../core/types.d.ts";
5
5
+
import { applet, reactive } from "../../../scripts/theme";
6
6
+
7
7
+
////////////////////////////////////////////
8
8
+
// SETUP
9
9
+
////////////////////////////////////////////
10
10
+
// Register applet
11
11
+
const context = applets.register<Output>();
12
12
+
13
13
+
// Applet connections
14
14
+
const configurator = {
15
15
+
storage: {
16
16
+
output: await applet("../../configurator/storage/output", { context: self.parent }),
17
17
+
},
18
18
+
};
19
19
+
20
20
+
// Sample content
21
21
+
const SAMPLE_SOURCE = {
22
22
+
id: crypto.randomUUID(),
23
23
+
meta: {},
24
24
+
25
25
+
appletURI: "TODO",
26
26
+
};
27
27
+
28
28
+
const SAMPLE_TRACKS: Track[] = [
29
29
+
{
30
30
+
id: crypto.randomUUID(),
31
31
+
sourceId: SAMPLE_SOURCE.id,
32
32
+
uri: "https://archive.org/download/SUSPENSE_Radio_Digitally_Restored_Collection/%2040-07-22%20The%20Lodger%20%28audition%29%20%28Herbert%20Marshall%2C%20Alfred%20Hitchcock%2C%20Edmund%20Gwenn%29.mp3",
33
33
+
tags: {
34
34
+
title: "Yours Truly, Johnny Dollar",
35
35
+
},
36
36
+
},
37
37
+
{
38
38
+
id: crypto.randomUUID(),
39
39
+
sourceId: SAMPLE_SOURCE.id,
40
40
+
uri: "https://archive.org/download/OTRR_Dimension_X_Singles/Dimension_X_1950-04-08__01_OuterLimit.mp3",
41
41
+
tags: {
42
42
+
title: "Dimension X",
43
43
+
},
44
44
+
},
45
45
+
];
46
46
+
47
47
+
// Initial state
48
48
+
context.data = {
49
49
+
sources: await loadSources(),
50
50
+
tracks: await loadTracks(),
51
51
+
};
52
52
+
53
53
+
////////////////////////////////////////////
54
54
+
// LOADERS
55
55
+
////////////////////////////////////////////
56
56
+
async function loadSources(): Promise<Source[]> {
57
57
+
// TODO: This is not concurrency safe!
58
58
+
await configurator.storage.output.sendAction("get", {
59
59
+
name: "sources",
60
60
+
});
61
61
+
62
62
+
const data = configurator.storage.output.data;
63
63
+
if (!data) return [SAMPLE_SOURCE];
64
64
+
return decode(data as Uint8Array);
65
65
+
}
66
66
+
67
67
+
async function loadTracks(): Promise<Track[]> {
68
68
+
// TODO: This is not concurrency safe!
69
69
+
await configurator.storage.output.sendAction("get", {
70
70
+
name: "tracks",
71
71
+
});
72
72
+
73
73
+
const data = configurator.storage.output.data;
74
74
+
if (!data) return SAMPLE_TRACKS;
75
75
+
return decode(data as Uint8Array);
76
76
+
}
77
77
+
78
78
+
////////////////////////////////////////////
79
79
+
// ACTIONS
80
80
+
////////////////////////////////////////////
81
81
+
82
82
+
// UPDATE SOURCES: addSource, removeSource → SAVE_SOURCES
83
83
+
// UPDATE TRACKS: addTracks → SAVE_TRACKS
84
84
+
85
85
+
function saveSources(sources: Source[]) {
86
86
+
const data = encode(sources);
87
87
+
88
88
+
configurator.storage.output.sendAction("put", {
89
89
+
name: "sources",
90
90
+
data,
91
91
+
});
92
92
+
}
93
93
+
94
94
+
function saveTracks(tracks: Track[]) {
95
95
+
const data = encode(tracks);
96
96
+
97
97
+
configurator.storage.output.sendAction("put", {
98
98
+
name: "tracks",
99
99
+
data,
100
100
+
});
101
101
+
}
102
102
+
103
103
+
////////////////////////////////////////////
104
104
+
// 🛠️
105
105
+
////////////////////////////////////////////
106
106
+
function decode(data: Uint8Array) {
107
107
+
return JSON.parse(new TextDecoder().decode(data));
108
108
+
}
109
109
+
110
110
+
function encode(data: Object) {
111
111
+
return new TextEncoder().encode(JSON.stringify(data));
112
112
+
}
113
113
+
</script>
114
114
+
115
115
+
<style>
116
116
+
iframe {
117
117
+
display: none;
118
118
+
}
119
119
+
</style>
+6
src/applets/orchestrator/storage/manifest.json
Reviewed
···
1
1
+
{
2
2
+
"name": "diffuse/orchestrator/storage",
3
3
+
"title": "Diffuse Orchestrator | Storage",
4
4
+
"entrypoint": "index.html",
5
5
+
"actions": {}
6
6
+
}
-58
src/applets/storage/output/memory/applet.astro
Reviewed
···
1
1
-
<script>
2
2
-
import { applets } from "@web-applets/sdk";
3
3
-
4
4
-
import type { Output, Track } from "../../../core/types.d.ts";
5
5
-
import { applet, reactive } from "../../../../scripts/theme";
6
6
-
7
7
-
////////////////////////////////////////////
8
8
-
// SETUP
9
9
-
////////////////////////////////////////////
10
10
-
const context = applets.register<Output>();
11
11
-
12
12
-
const SAMPLE_SOURCE = {
13
13
-
id: crypto.randomUUID(),
14
14
-
meta: {},
15
15
-
16
16
-
appletURI: "TODO",
17
17
-
};
18
18
-
19
19
-
const SAMPLE_TRACKS: Track[] = [
20
20
-
{
21
21
-
id: crypto.randomUUID(),
22
22
-
sourceId: SAMPLE_SOURCE.id,
23
23
-
uri: "https://archive.org/download/SUSPENSE_Radio_Digitally_Restored_Collection/%2040-07-22%20The%20Lodger%20%28audition%29%20%28Herbert%20Marshall%2C%20Alfred%20Hitchcock%2C%20Edmund%20Gwenn%29.mp3",
24
24
-
tags: {
25
25
-
title: "Yours Truly, Johnny Dollar",
26
26
-
},
27
27
-
},
28
28
-
{
29
29
-
id: crypto.randomUUID(),
30
30
-
sourceId: SAMPLE_SOURCE.id,
31
31
-
uri: "https://archive.org/download/OTRR_Dimension_X_Singles/Dimension_X_1950-04-08__01_OuterLimit.mp3",
32
32
-
tags: {
33
33
-
title: "Dimension X",
34
34
-
},
35
35
-
},
36
36
-
];
37
37
-
38
38
-
// Initial state
39
39
-
context.data = {
40
40
-
sources: [SAMPLE_SOURCE],
41
41
-
tracks: SAMPLE_TRACKS, // []
42
42
-
};
43
43
-
44
44
-
// State helpers
45
45
-
function update(partial: Partial<Output>): void {
46
46
-
context.data = { ...context.data, ...partial };
47
47
-
}
48
48
-
49
49
-
////////////////////////////////////////////
50
50
-
// ACTIONS
51
51
-
////////////////////////////////////////////
52
52
-
</script>
53
53
-
54
54
-
<style>
55
55
-
iframe {
56
56
-
display: none;
57
57
-
}
58
58
-
</style>
-6
src/applets/storage/output/memory/manifest.json
Reviewed
···
1
1
-
{
2
2
-
"name": "diffuse/storage/output/memory",
3
3
-
"title": "Diffuse Storage | Output | Memory",
4
4
-
"entrypoint": "index.html",
5
5
-
"actions": {}
6
6
-
}
+5
-7
src/applets/themes/pilot/ui/audio/applet.astro
Reviewed
···
1
1
-
<link
2
2
-
rel="stylesheet"
3
3
-
href="https://cdn.jsdelivr.net/gh/iconoir-icons/iconoir@main/css/iconoir.css"
4
4
-
/>
5
5
-
6
1
<main>
7
2
<div class="queue-entry"></div>
8
3
<div class="playback-info">
···
20
15
21
16
<style>
22
17
@import "../../../../../styles/themes/pilot/variables.css";
18
18
+
@import "../../../../../styles/icons.css";
23
19
24
20
main {
25
21
align-items: center;
···
91
87
</style>
92
88
93
89
<script>
90
90
+
// @ts-ignore
91
91
+
import scope from "astro:scope";
94
92
import { applets } from "@web-applets/sdk";
95
93
import { State } from "./types";
96
94
···
131
129
132
130
function render() {
133
131
document.body.querySelector("button").innerHTML = context.data.isPlaying
134
134
-
? `<i class="iconoir-pause-solid"></i>`
135
135
-
: `<i class="iconoir-play-solid"></i>`;
132
132
+
? `<i class="iconoir-pause-solid" data-astro-cid-${scope}></i>`
133
133
+
: `<i class="iconoir-play-solid" data-astro-cid-${scope}></i>`;
136
134
}
137
135
</script>
+19
-4
src/pages/index.astro
Reviewed
···
4
4
// import "@picocss/pico/css/pico.colors.css";
5
5
import "../styles/pages/index.css";
6
6
7
7
+
const configurators = [{ url: "configurator/storage/output/", title: "Storage / Output" }];
8
8
+
7
9
const engines = [
8
10
{ url: "engine/audio/", title: "Audio" },
9
11
{ url: "engine/queue/", title: "Queue" },
10
12
];
11
13
12
12
-
const orchestrators = [{ url: "orchestrator/queue/", title: "Queue" }];
14
14
+
const orchestrators = [
15
15
+
{ url: "orchestrator/queue/", title: "Queue" },
16
16
+
{ url: "orchestrator/storage/", title: "Storage" },
17
17
+
];
13
18
14
14
-
const storages = [{ url: "storage/output/memory/", title: "Output / Memory" }];
19
19
+
const storages = [];
15
20
16
21
const themes = [{ url: "themes/pilot/", title: "Pilot" }];
17
22
---
···
76
81
<p>
77
82
<em
78
83
>These too are applet compositions. However, unlike themes, these are purely logical, and
79
79
-
optional. Mostly exist in order to construct sensible defaults (eg. applet connections you
80
80
-
want to reuse across themes).</em
84
84
+
reuse applet instances from the parent context (when available). Mostly exist in order to
85
85
+
construct sensible defaults (eg. applet connections you want to reuse across themes).</em
81
86
>
82
87
</p>
83
88
···
128
133
that takes the same actions and data output.</em
129
134
>
130
135
</p>
136
136
+
137
137
+
<ul>
138
138
+
{
139
139
+
configurators.map((item: any) => (
140
140
+
<li>
141
141
+
<a href={item.url}>{item.title}</a>
142
142
+
</li>
143
143
+
))
144
144
+
}
145
145
+
</ul>
131
146
132
147
<h3>Supplements</h3>
133
148
+9
-41
src/scripts/themes/pilot/index.ts
Reviewed
···
1
1
+
import type { Output } from "../../../applets/core/types.d.ts";
1
2
import { applet, comparable, reactive } from "../../theme.ts";
2
3
3
4
////////////////////////////////////////////
···
8
9
////////////////////////////////////////////
9
10
// 🗂️ Applets
10
11
////////////////////////////////////////////
11
11
-
import type { Output, Track } from "../../../applets/core/types.d.ts";
12
12
-
13
12
import type * as AudioEngine from "../../../applets/engine/audio/types.d.ts";
14
13
import type * as QueueEngine from "../../../applets/engine/queue/types.d.ts";
15
14
16
15
import type * as AudioUI from "../../../applets/themes/pilot/ui/audio/types.ts";
17
16
17
17
+
const configurator = {
18
18
+
storage: {
19
19
+
output: await applet("../../configurator/storage/output"),
20
20
+
},
21
21
+
};
22
22
+
18
23
const engine = {
19
24
audio: await applet<AudioEngine.State>("../../engine/audio"),
20
25
queue: await applet<QueueEngine.State>("../../engine/queue"),
···
22
27
23
28
const orchestrator = {
24
29
queue: await applet("../../orchestrator/queue"),
25
25
-
};
26
26
-
27
27
-
const storage = {
28
28
-
output: {
29
29
-
memory: await applet<Output>("../../storage/output/memory"),
30
30
-
},
30
30
+
storage: await applet<Output>("../../orchestrator/storage"),
31
31
};
32
32
33
33
const ui = {
···
64
64
);
65
65
66
66
////////////////////////////////////////////
67
67
-
// 📦 [Connections → Storages/Output]
68
68
-
// 🧠 Memory
69
69
-
////////////////////////////////////////////
70
70
-
71
71
-
// Track changes to in-memory user data,
72
72
-
// reflect to local & remote data stores.
73
73
-
// TODO: Make configurator applet.
74
74
-
// Move to orchestrator?
75
75
-
76
76
-
reactive(
77
77
-
storage.output.memory,
78
78
-
(data) => comparable(data),
79
79
-
(data) => {
80
80
-
// TODO: Store locally
81
81
-
// TODO: Store remotely
82
82
-
//
83
83
-
// NOTE:
84
84
-
// Make data object an automerge document
85
85
-
// and compact on every page load?
86
86
-
// We should be able to track what property
87
87
-
// of the data has changed.
88
88
-
},
89
89
-
);
90
90
-
91
91
-
// NOTE: How do we sync new remote data with our local/in-memory data?
92
92
-
//
93
93
-
// storage.output.memory.data = {
94
94
-
// sources: [],
95
95
-
// tracks: []
96
96
-
// }
97
97
-
98
98
-
////////////////////////////////////////////
99
67
// 🌅 [Connections → UI]
100
68
// 🔉 AUDIO
101
69
////////////////////////////////////////////
···
109
77
110
78
// Automatically start playing something if nothing is playing yet.
111
79
if (!trackId) {
112
112
-
if (isPlaying) engine.queue.sendAction("shift", null);
80
80
+
if (isPlaying) engine.queue.sendAction("shift");
113
81
return;
114
82
}
115
83
+3
src/styles/configurator.css
Reviewed
···
1
1
+
@import "@picocss/pico/css/pico.css";
2
2
+
@import "@picocss/pico/css/pico.colors.css";
3
3
+
@import "./icons.css";
+1
src/styles/icons.css
Reviewed
···
1
1
+
@import "iconoir/css/iconoir.css";