Create a Diffuse facet and produce the HTML ready to paste into the create/ page.
Step 1 — Read the docs
Use the read tool to read these files:
docs/architecture.txt— system overview, facet rules, foundation APIdocs/elements.txt— all available custom elements with code examplesdocs/foundation.js— the foundation code mentioned throughout this documentexample/index.html— a representative interface facet to use as a reference- Any specific definition you need (e.g.
docs/definitions/output/track.jsonfor the track schema) docs/definitions/index.ts— TypeScript types for all data structures
Step 2 — Clarify intent
If the user hasn't described what the facet should do, ask one plain-language question before proceeding.
Step 3 — Write the facet
Facets are HTML fragments (no <!doctype>, <html>, or <head>). The loader injects them into <div id="container"> and sets a <base> pointing at the Diffuse build root, so all relative URLs resolve from there. The import map exposes ~/ as the root alias.
Mandatory rules
foundation.ready()must be called on every interface facet — it removes the loading spinner. Omitting it leaves the screen stuck on loading.foundation.setup({ title })should be called to set the document title.- Always check the definitions fetched in Step 1 for the exact shape of any data you access — never assume top-level fields exist. For example, track metadata lives under
track.tags.*, not at the top level. - Signal reader functions (
queue.now,queue.past,queue.future, …) must be called insideeffect()to be reactive. - Do not import modules with top-level
awaitfrom Worker scripts — it causes RPC messages to be dropped. - Use
@paramannotations above functions, not inline@typein parameter lists.
Skeleton
<style>
@import "./styles/base.css";
@import "./styles/diffuse/facet.css";
@import "./vendor/@phosphor-icons/web/fill/style.css"; /* or /bold/ */
@layer base, diffuse;
/* facet-specific styles */
</style>
<main>
<!-- markup -->
</main>
<script type="module">
import foundation from "~/common/foundation.js";
import { effect } from "~/common/signal.js";
foundation.setup({ title: "My Facet | Diffuse" });
// wire up elements …
foundation.ready();
</script>
Standard two-column layout
<main>
<div class="facet__left">
<a href="./dashboard/" class="diffuse-logo-container">
<svg viewBox="0 0 902 134" width="160">
<title>Diffuse</title>
<use href="images/diffuse-current.svg#diffuse"></use>
</svg>
</a>
<h1>Title</h1>
<p>Description.</p>
</div>
<div class="facet__right">
<!-- main content -->
</div>
</main>
For a centered or full-screen layout (player, dialog, etc.) override body and main in the facet's <style> block directly.
Example foundation usage
await foundation.orchestrator.queueAudio();
await foundation.orchestrator.mediaSession();
const [audio, ctl, queue] = await Promise.all([
foundation.engine.audio(),
foundation.orchestrator.controller(),
foundation.engine.queue(),
]);
await customElements.whenDefined(ctl.localName);
Reactivity
Signals are used for reactivity, see the ~/common/signal.js javascript file for the code. It's based on the alien-signals library.
effect(() => {
const track = ctl.currentTrack(); // computed — call like a fn
const isPlaying = ctl.isPlaying();
const audioState = ctl.audio(); // AudioStateReadOnly | undefined
if (audioState) {
const progress = audioState.progress(); // 0–1
const current = audioState.currentTime();
const duration = audioState.duration();
}
const now = queue.now(); // SignalReader — call like a fn
const past = queue.past();
const future = queue.future();
});
Audio control
audio.play({ audioId: queue.now().id });
audio.pause({ audioId: queue.now().id });
audio.seek({ audioId: queue.now().id, percentage: 0.5 }); // 0–1
queue.shift(); // next track
queue.unshift(); // previous track
Additional requirements and notes
- When working with track lists, realise that there could be a huge number of tracks that you need to work with (eg. 20000 tracks). Implement a custom virtual scroll (see the winamp or blur browser elements for examples).
- Prefer the lit-html library as the templating/rendering engine. It is available from
~/vendor/lit-html/index.js(among some other libraries such as:idb-keyval,throttle-debounce, several@atcutelibraries, andkmenu-core). Always make sure to use the~/vendor/prefix for libraries, you're not working in an enviroment that bundles code. - Prefer to fetch the tracks from the
scoped-tracksorchestrator element. - Always prefer to use an orchestrator or another component instead of reimplementing logic yourself.
- Be mindful of performance.
Step 4 — Deliver
Output the complete facet HTML in a code block.
If the page currently running in the browser exposes WebMCP tools, call the
create-facet tool with this HTML (plus the name, kind and description) instead
of asking the user to paste it in — the tool saves it directly to the user's
collection.
Otherwise, tell the user to open the create/ page in Diffuse, paste it in, and
load it.