tangled
alpha
login
or
join now
tokono.ma
/
diffuse
5
fork
atom
A music player that connects to your cloud/distributed storage.
5
fork
atom
overview
issues
4
pulls
pipelines
feat: scrobble feature facet + copy facet descriptions
Steven Vandevelde
1 day ago
e372ea2a
b447789a
+60
-13
10 changed files
expand all
collapse all
unified
split
src
_components
facets
grid.vto
_data
facets.yaml
common
facets
foundation.js
utils.js
facets
common
build.js
grid.js
you.js
scrobble
index.html
index.inline.js
styles
diffuse
page.css
+2
src/_components/facets/grid.vto
···
18
18
<li
19
19
class="grid-item"
20
20
data-active-color="{{color}}"
21
21
+
data-description="{{item.desc.trim()}}"
21
22
data-name="{{item.title}}"
23
23
+
data-kind="{{item.kind ?? `interface`}}"
22
24
data-uri="{{ item.url |> facetOrThemeURI }}"
23
25
>
24
26
<div class="grid-item__contents">
+7
src/_data/facets.yaml
···
10
10
featured: true
11
11
desc: >
12
12
Automatically put tracks into the queue.
13
13
+
- url: "facets/scrobble/index.html"
14
14
+
title: "Scrobble"
15
15
+
kind: "prelude"
16
16
+
category: Misc
17
17
+
featured: true
18
18
+
desc: >
19
19
+
Enable scrobbling, keep track of what you're listening to.
13
20
- url: "facets/scrobble/last.fm/index.html"
14
21
title: "Scrobble / Last.fm"
15
22
category: Misc
+1
-3
src/common/facets/foundation.js
···
82
82
}
83
83
84
84
async function playAudioFromQueue() {
85
85
-
const [a, q, ms, qa, sca] = await Promise.all([
85
85
+
const [a, q, ms, qa] = await Promise.all([
86
86
// engine
87
87
audio(),
88
88
queue(),
···
90
90
// orchestrator
91
91
mediaSession(),
92
92
queueAudio(),
93
93
-
scrobbleAudio(),
94
93
]);
95
94
96
95
return {
···
101
100
orchestrator: {
102
101
mediaSession: ms,
103
102
queueAudio: qa,
104
104
-
scrobbleAudio: sca,
105
103
},
106
104
};
107
105
}
+7
-2
src/common/facets/utils.js
···
10
10
*/
11
11
12
12
/**
13
13
-
* @param {{ name: string; uri: string }} _args
13
13
+
* @param {{ description?: string; kind: string | undefined; name: string; uri: string }} _args
14
14
* @param {{ fetchHTML: boolean }} options
15
15
*/
16
16
-
export async function facetFromURI({ name, uri }, { fetchHTML }) {
16
16
+
export async function facetFromURI(
17
17
+
{ description, kind, name, uri },
18
18
+
{ fetchHTML },
19
19
+
) {
17
20
const html = fetchHTML ? await loadURI(uri) : undefined;
18
21
const cid = html
19
22
? await CID.create(0x55, new TextEncoder().encode(html))
···
26
29
createdAt: timestamp,
27
30
id: TID.now(),
28
31
cid,
32
32
+
description,
29
33
html,
30
34
name,
35
35
+
kind: kind === "interactive" || kind === "prelude" ? kind : undefined,
31
36
updatedAt: timestamp,
32
37
uri,
33
38
};
+7
-2
src/facets/common/build.js
···
105
105
const cid = await CID.create(0x55, new TextEncoder().encode(html));
106
106
const name = nameEl?.value ?? "nameless";
107
107
const description = descriptionEl?.value ?? "";
108
108
-
const kind = /** @type {"interactive" | "prelude"} */ (kindEl?.value ?? "interactive");
108
108
+
const kind =
109
109
+
/** @type {"interactive" | "prelude"} */ (kindEl?.value ?? "interactive");
109
110
110
111
/** @type {Facet} */
111
112
const facet = $editingFacet.value
···
231
232
const name = target.closest("li")?.getAttribute("data-name");
232
233
if (!name) return;
233
234
235
235
+
const kind = target.closest("li")?.getAttribute("data-kind") ?? undefined;
236
236
+
234
237
switch (rel) {
235
238
case "edit": {
236
236
-
const facet = await facetFromURI({ name, uri }, { fetchHTML: true });
239
239
+
const facet = await facetFromURI({ kind, name, uri }, {
240
240
+
fetchHTML: true,
241
241
+
});
237
242
editFacet(facet);
238
243
document.querySelector("#build")?.scrollIntoView();
239
244
break;
+6
-1
src/facets/common/grid.js
···
26
26
27
27
const uri = li.getAttribute("data-uri");
28
28
const name = li.getAttribute("data-name");
29
29
+
const kind = li.getAttribute("data-kind") ?? undefined;
30
30
+
const description = li.getAttribute("data-description") ?? undefined;
31
31
+
29
32
if (!uri || !name) return;
30
33
31
34
const out = await foundation.orchestrator.output();
···
35
38
if (isActive) {
36
39
out.facets.save(collection.filter((f) => f.uri !== uri));
37
40
} else {
38
38
-
const facet = await facetFromURI({ name, uri }, { fetchHTML: false });
41
41
+
const facet = await facetFromURI({ description, kind, name, uri }, {
42
42
+
fetchHTML: false,
43
43
+
});
39
44
out.facets.save([...collection, facet]);
40
45
}
41
46
});
+22
-1
src/facets/common/you.js
···
87
87
/>
88
88
</div>
89
89
<div>
90
90
+
<label>Kind</label>
91
91
+
<select id="add-uri-kind">
92
92
+
<option value="interactive">interface</option>
93
93
+
<option value="prelude">feature</option>
94
94
+
</select>
95
95
+
</div>
96
96
+
<div>
90
97
<label>URI</label>
91
98
<input
92
99
id="add-uri-uri"
···
118
125
"submit",
119
126
async (e) => {
120
127
e.preventDefault();
128
128
+
121
129
const nameEl = /** @type {HTMLInputElement} */ (
122
130
dialog?.querySelector("#add-uri-name")
123
131
);
132
132
+
133
133
+
const kindEl = /** @type {HTMLSelectElement} */ (
134
134
+
dialog?.querySelector("#add-uri-kind")
135
135
+
);
136
136
+
124
137
const uriEl = /** @type {HTMLInputElement} */ (
125
138
dialog?.querySelector("#add-uri-uri")
126
139
);
140
140
+
127
141
const name = nameEl?.value.trim() ?? "";
142
142
+
const kind = kindEl?.value ?? "interactive";
128
143
const uri = uriEl?.value.trim() ?? "";
129
144
if (!name || !uri) return;
130
130
-
const facet = await facetFromURI({ name, uri }, { fetchHTML: false });
145
145
+
146
146
+
const facet = await facetFromURI({ kind, name, uri }, { fetchHTML: false });
131
147
await saveFacet(facet);
148
148
+
132
149
/** @type {HTMLDialogElement} */ (dialog).close();
133
150
},
134
151
);
···
137
154
const nameEl = /** @type {HTMLInputElement} */ (
138
155
dialog.querySelector("#add-uri-name")
139
156
);
157
157
+
const kindEl = /** @type {HTMLSelectElement} */ (
158
158
+
dialog.querySelector("#add-uri-kind")
159
159
+
);
140
160
const uriEl = /** @type {HTMLInputElement} */ (
141
161
dialog.querySelector("#add-uri-uri")
142
162
);
143
163
if (nameEl) nameEl.value = "";
164
164
+
if (kindEl) kindEl.value = "interactive";
144
165
if (uriEl) uriEl.value = "";
145
166
146
167
dialog.showModal();
+1
src/facets/scrobble/index.html
···
1
1
+
<script type="module" src="./index.inline.js"></script>
+2
src/facets/scrobble/index.inline.js
···
1
1
+
import foundation from "~/common/facets/foundation.js";
2
2
+
await foundation.orchestrator.scrobbleAudio();
+5
-4
src/styles/diffuse/page.css
···
225
225
select {
226
226
appearance: none;
227
227
background: transparent;
228
228
-
border: 2px solid var(--form-color);
229
229
-
border-radius: var(--radius-md);
228
228
+
border: 3px solid var(--form-color);
229
229
+
border-radius: var(--radius-sm);
230
230
color: inherit;
231
231
font-family: inherit;
232
232
font-size: var(--fs-sm);
···
304
304
border-radius: 9999px;
305
305
display: inline-block;
306
306
font-size: var(--fs-2xs);
307
307
-
font-weight: 500;
308
308
-
padding: 1px var(--space-3xs);
307
307
+
font-weight: 600;
308
308
+
padding: var(--space-3xs);
309
309
+
text-box: trim-both cap alphabetic;
309
310
vertical-align: middle;
310
311
}
311
312