hci-atrium 0.1.0 → 0.3.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 +266 -8
- package/dist/asset-meta.d.ts +31 -0
- package/dist/asset-meta.d.ts.map +1 -0
- package/dist/asset-meta.js +25 -0
- package/dist/asset-meta.js.map +1 -0
- package/dist/atrium.d.ts +220 -7
- package/dist/atrium.d.ts.map +1 -1
- package/dist/atrium.js +410 -39
- package/dist/atrium.js.map +1 -1
- package/dist/dash.d.ts +206 -0
- package/dist/dash.d.ts.map +1 -0
- package/dist/dash.js +302 -0
- package/dist/dash.js.map +1 -0
- package/dist/direct.d.ts +86 -0
- package/dist/direct.d.ts.map +1 -0
- package/dist/direct.js +121 -0
- package/dist/direct.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +70 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +56 -0
- package/dist/models.js.map +1 -1
- package/dist/player/audio-layers.d.ts +306 -0
- package/dist/player/audio-layers.d.ts.map +1 -0
- package/dist/player/audio-layers.js +1245 -0
- package/dist/player/audio-layers.js.map +1 -0
- package/dist/player/index.d.ts +13 -0
- package/dist/player/index.d.ts.map +1 -0
- package/dist/player/index.js +13 -0
- package/dist/player/index.js.map +1 -0
- package/dist/player/layer-signals.d.ts +80 -0
- package/dist/player/layer-signals.d.ts.map +1 -0
- package/dist/player/layer-signals.js +174 -0
- package/dist/player/layer-signals.js.map +1 -0
- package/dist/player/play-command.d.ts +55 -0
- package/dist/player/play-command.d.ts.map +1 -0
- package/dist/player/play-command.js +36 -0
- package/dist/player/play-command.js.map +1 -0
- package/dist/player/play-intent.d.ts +18 -0
- package/dist/player/play-intent.d.ts.map +1 -0
- package/dist/player/play-intent.js +26 -0
- package/dist/player/play-intent.js.map +1 -0
- package/dist/player/runtime.d.ts +105 -0
- package/dist/player/runtime.d.ts.map +1 -0
- package/dist/player/runtime.js +402 -0
- package/dist/player/runtime.js.map +1 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-room-connection.d.ts +40 -0
- package/dist/react/use-room-connection.d.ts.map +1 -0
- package/dist/react/use-room-connection.js +85 -0
- package/dist/react/use-room-connection.js.map +1 -0
- package/dist/rooms/recorder.d.ts +4 -0
- package/dist/rooms/recorder.d.ts.map +1 -1
- package/dist/rooms/recorder.js +7 -0
- package/dist/rooms/recorder.js.map +1 -1
- package/dist/rooms/room.d.ts +78 -0
- package/dist/rooms/room.d.ts.map +1 -1
- package/dist/rooms/room.js +208 -3
- package/dist/rooms/room.js.map +1 -1
- package/dist/rooms/standing-source.d.ts +3 -0
- package/dist/rooms/standing-source.d.ts.map +1 -1
- package/dist/rooms/standing-source.js +6 -0
- package/dist/rooms/standing-source.js.map +1 -1
- package/dist/rooms/transcriber.d.ts +4 -0
- package/dist/rooms/transcriber.d.ts.map +1 -1
- package/dist/rooms/transcriber.js +1 -0
- package/dist/rooms/transcriber.js.map +1 -1
- package/dist/shaping/index.d.ts +1 -0
- package/dist/shaping/index.d.ts.map +1 -1
- package/dist/shaping/index.js +1 -0
- package/dist/shaping/index.js.map +1 -1
- package/dist/shaping/request.d.ts +1 -1
- package/dist/shaping/request.d.ts.map +1 -1
- package/dist/shaping/surveys.d.ts +69 -0
- package/dist/shaping/surveys.d.ts.map +1 -0
- package/dist/shaping/surveys.js +129 -0
- package/dist/shaping/surveys.js.map +1 -0
- package/dist/tools.d.ts +84 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +105 -0
- package/dist/tools.js.map +1 -0
- package/dist/trace.d.ts +96 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +251 -0
- package/dist/trace.js.map +1 -0
- package/dist/transcribe.d.ts +5 -0
- package/dist/transcribe.d.ts.map +1 -1
- package/dist/transcribe.js +1 -1
- package/dist/transcribe.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { LayerEngine, type LayerState } from "./audio-layers.js";
|
|
2
|
+
import { type SendSignalOpts } from "./layer-signals.js";
|
|
3
|
+
/** A room frame as it reaches the runtime — the transport layer parses the wire envelope and hands
|
|
4
|
+
* this shape to `handleSignal`. */
|
|
5
|
+
export interface PlayerSignal {
|
|
6
|
+
type?: string;
|
|
7
|
+
payload?: unknown;
|
|
8
|
+
/** The sender's connection id (server-stamped) — the reply target for a `layer.query`. */
|
|
9
|
+
from?: string;
|
|
10
|
+
/** A request's correlation id, echoed back as `reply_to` on the answer. */
|
|
11
|
+
ack_id?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PlayerRuntimeOptions {
|
|
14
|
+
/** A live AudioContext (created by the host on its Start gesture — autoplay policy). */
|
|
15
|
+
context: AudioContext;
|
|
16
|
+
/** Where the master chain's output routes. Defaults to `context.destination`. The runtime always
|
|
17
|
+
* builds its own master gain → limiter and exposes `masterGain` regardless. */
|
|
18
|
+
destination?: AudioNode;
|
|
19
|
+
/** Asset-resolution root for the `asset_id`-only play shape. `""` (default) = same-origin. */
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
/** Outbound-signal sink — the host POSTs it into the room. Used for `layer.query` replies and
|
|
22
|
+
* `layer.state` broadcasts; the runtime never builds the wire envelope itself. */
|
|
23
|
+
send: (type: string, payload: unknown, opts?: SendSignalOpts) => void;
|
|
24
|
+
/** Human-readable engine/dispatch breadcrumbs (the durable record of a room is its session). */
|
|
25
|
+
onLog?: (text: string) => void;
|
|
26
|
+
/** Called on every engine change with the fresh snapshot — the host reflects it into its UI. */
|
|
27
|
+
onSnapshot?: (layers: Record<string, LayerState>) => void;
|
|
28
|
+
/** Called when a `play` resolves a bytes track, so the host can seed a visualizer with the sound's
|
|
29
|
+
* name + (when known) tempo — the runtime is framework-agnostic and can't reach the analyzer. Fires
|
|
30
|
+
* for a legacy flat play's one-shot AND the unified path's first bytes track (queue or oneshot);
|
|
31
|
+
* `bpm` is only present on the flat path (a resolved LayerTrack carries no tempo). */
|
|
32
|
+
onTrack?: (track: {
|
|
33
|
+
name: string;
|
|
34
|
+
bpm?: number;
|
|
35
|
+
}) => void;
|
|
36
|
+
/** Escape hatch for intents the runtime can't play (Spotify, "open link") and any message type it
|
|
37
|
+
* doesn't recognize. Return true when the host consumed it; false logs-and-skips. The frame it
|
|
38
|
+
* receives always carries `layer`/`from`/`ack_id` when present: a flat play forwards the original
|
|
39
|
+
* frame verbatim; a unified play's Spotify track is re-synthesized with the resolved `layer` and the
|
|
40
|
+
* original frame's `from`/`ack_id`, so the host has the same routing context on both paths. */
|
|
41
|
+
unhandled?: (msg: PlayerSignal) => boolean;
|
|
42
|
+
/** Loudness-normalize tracks carrying a measured LUFS (default true — passed to the engine). */
|
|
43
|
+
normalize?: boolean;
|
|
44
|
+
/** Subscriber set (`layer.subscribe`rs, by connection id) to adopt BY REFERENCE — host and runtime
|
|
45
|
+
* mutate the same Set, so a host can carry over subscribers it tracked before the runtime existed
|
|
46
|
+
* (the pre-Start window). The runtime never replaces or clears it; the host owns its lifecycle. The
|
|
47
|
+
* representation is intentionally a bare `Set<string>`, not a wrapper — both sides just add/delete. */
|
|
48
|
+
subscribers?: Set<string>;
|
|
49
|
+
/** Decoded-buffer cache (keyed by content URL) to adopt BY REFERENCE, like `subscribers` — lets a
|
|
50
|
+
* host keep the warm cache across runtime rebuilds (e.g. a Stop→Start), so replayed sounds don't
|
|
51
|
+
* re-fetch/re-decode. Defaults to a fresh per-runtime Map. */
|
|
52
|
+
buffers?: Map<string, AudioBuffer>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The headless Player: feed it room frames via {@link handleSignal}, give it a `send` sink, and it
|
|
56
|
+
* plays the `play` / `layer.*` dialect over WebAudio. Owns the master chain + {@link LayerEngine};
|
|
57
|
+
* the host owns transport, UI, and any Spotify/link glue (reached through `unhandled`).
|
|
58
|
+
*/
|
|
59
|
+
export declare class PlayerRuntime {
|
|
60
|
+
/** The master gain every layer routes through — the analyzer's tap point (pre-limiter). */
|
|
61
|
+
readonly masterGain: GainNode;
|
|
62
|
+
/** The layered-audio engine. Exposed so the host can drive listener-side controls (per-layer /
|
|
63
|
+
* master volume, normalization, device-change resume) and read snapshots for its own ticker. */
|
|
64
|
+
readonly engine: LayerEngine;
|
|
65
|
+
private readonly opts;
|
|
66
|
+
private readonly ctx;
|
|
67
|
+
private readonly limiter;
|
|
68
|
+
private readonly engineUnsub;
|
|
69
|
+
private readonly subscribers;
|
|
70
|
+
private readonly buffers;
|
|
71
|
+
private playChain;
|
|
72
|
+
private broadcastTimer;
|
|
73
|
+
private heartbeatTimer;
|
|
74
|
+
private disposed;
|
|
75
|
+
constructor(opts: PlayerRuntimeOptions);
|
|
76
|
+
/**
|
|
77
|
+
* Dispatch a single room frame. The full `play` / `layer.*` dialect; `room.code_rotated` and other
|
|
78
|
+
* transport-terminal frames stay with the host (they never reach here). An unrecognized type falls
|
|
79
|
+
* through to the `unhandled` hook.
|
|
80
|
+
*/
|
|
81
|
+
handleSignal(msg: PlayerSignal): void;
|
|
82
|
+
/** Tear down the engine + the runtime's own timers. Leaves the AudioContext (the host created it)
|
|
83
|
+
* but disconnects the master chain this runtime built. Does not clear a shared subscriber set. */
|
|
84
|
+
dispose(): void;
|
|
85
|
+
private disconnect;
|
|
86
|
+
private loadBuffer;
|
|
87
|
+
private fetchAsset;
|
|
88
|
+
private resolveWireTracks;
|
|
89
|
+
private playBytesOneshot;
|
|
90
|
+
private handleFlatPlay;
|
|
91
|
+
private onPlaySignal;
|
|
92
|
+
private onLayerConfigure;
|
|
93
|
+
private onLayerClear;
|
|
94
|
+
private onLayerEffects;
|
|
95
|
+
private onLayerPosition;
|
|
96
|
+
private onLayerQuery;
|
|
97
|
+
private broadcastState;
|
|
98
|
+
private scheduleBroadcast;
|
|
99
|
+
private addSubscriber;
|
|
100
|
+
private removeSubscriber;
|
|
101
|
+
private updateHeartbeat;
|
|
102
|
+
private onEngineChange;
|
|
103
|
+
private log;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/player/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// The Player's dialect brain, lifted out of the hosted tool's page (tools/src/pages/player/main.tsx)
|
|
2
|
+
// so a host app (e.g. SoundMuse) can embed the same behavior. Framework-agnostic and
|
|
3
|
+
// transport-agnostic: it owns the WebAudio graph (master → limiter, the LayerEngine) and the full
|
|
4
|
+
// `play` / `layer.*` dispatch, but never opens a socket, touches the DOM, or imports React. The host
|
|
5
|
+
// wires transport by handing frames to `handleSignal` and supplying `send` (an outbound-signal sink);
|
|
6
|
+
// page-specific intents it can't play itself (Spotify, an "open link") route back out through the
|
|
7
|
+
// `unhandled` hook, so the runtime carries ZERO Spotify code.
|
|
8
|
+
//
|
|
9
|
+
// Design record: docs/plans/audio-layers.md ("The layer.* dialect") + docs/plans/streaming-assets.md
|
|
10
|
+
// ("Playback as a typed intent over rooms"). Pure signal parsing / track resolution lives in the
|
|
11
|
+
// sibling layer-signals.ts / play-command.ts / play-intent.ts modules.
|
|
12
|
+
import { fetchAssetMeta } from "../asset-meta.js";
|
|
13
|
+
import { httpFetch } from "../http.js";
|
|
14
|
+
import { LayerEngine, } from "./audio-layers.js";
|
|
15
|
+
import { carryTrackFields, fetchedLoudness, resolveWireTrack, validateTransition, wireTrackFetchId, } from "./layer-signals.js";
|
|
16
|
+
import { contentUrlFor, needsAssetFetch, resolvePlayback, } from "./play-command.js";
|
|
17
|
+
import { routePlayIntent } from "./play-intent.js";
|
|
18
|
+
/** How often a playing layer's elapsed/remaining clock is re-broadcast to subscribers (ms). */
|
|
19
|
+
const HEARTBEAT_MS = 1000;
|
|
20
|
+
/** Debounce window coalescing a burst of engine changes into one subscriber broadcast (ms). */
|
|
21
|
+
const BROADCAST_DEBOUNCE_MS = 200;
|
|
22
|
+
/**
|
|
23
|
+
* The headless Player: feed it room frames via {@link handleSignal}, give it a `send` sink, and it
|
|
24
|
+
* plays the `play` / `layer.*` dialect over WebAudio. Owns the master chain + {@link LayerEngine};
|
|
25
|
+
* the host owns transport, UI, and any Spotify/link glue (reached through `unhandled`).
|
|
26
|
+
*/
|
|
27
|
+
export class PlayerRuntime {
|
|
28
|
+
/** The master gain every layer routes through — the analyzer's tap point (pre-limiter). */
|
|
29
|
+
masterGain;
|
|
30
|
+
/** The layered-audio engine. Exposed so the host can drive listener-side controls (per-layer /
|
|
31
|
+
* master volume, normalization, device-change resume) and read snapshots for its own ticker. */
|
|
32
|
+
engine;
|
|
33
|
+
opts;
|
|
34
|
+
ctx;
|
|
35
|
+
limiter;
|
|
36
|
+
engineUnsub;
|
|
37
|
+
subscribers;
|
|
38
|
+
// Decoded-buffer cache, keyed by content URL — shared by the engine's one-shot + queue playback.
|
|
39
|
+
buffers;
|
|
40
|
+
// Unified `play` signals apply strictly in arrival order: resolution may await asset fetches, and
|
|
41
|
+
// unordered engine.enqueue calls would scramble queue semantics.
|
|
42
|
+
playChain = Promise.resolve();
|
|
43
|
+
broadcastTimer = null;
|
|
44
|
+
heartbeatTimer = null;
|
|
45
|
+
// Set by dispose(); every await-then-touch-the-engine path re-checks it so a frame in flight when
|
|
46
|
+
// the host tears the runtime down can't resurrect the disposed engine.
|
|
47
|
+
disposed = false;
|
|
48
|
+
constructor(opts) {
|
|
49
|
+
this.opts = opts;
|
|
50
|
+
this.ctx = opts.context;
|
|
51
|
+
// Both adopted by reference (see the option docs): the runtime mutates these in place and never
|
|
52
|
+
// replaces or clears them, so a host's pre-Start subscribers / warm buffer cache survive intact.
|
|
53
|
+
this.subscribers = opts.subscribers ?? new Set();
|
|
54
|
+
this.buffers = opts.buffers ?? new Map();
|
|
55
|
+
// A single master node every sound routes through, so the analyzer can tap the summed playback.
|
|
56
|
+
// A brick-wall-ish limiter on the master output is a clipping safety net for normalization boosts
|
|
57
|
+
// and stacked one-shots. The analyzer taps `master` (pre-limiter), so shared analysis still
|
|
58
|
+
// reflects the intended mix.
|
|
59
|
+
const master = this.ctx.createGain();
|
|
60
|
+
const limiter = this.ctx.createDynamicsCompressor();
|
|
61
|
+
limiter.threshold.value = -3;
|
|
62
|
+
limiter.knee.value = 0;
|
|
63
|
+
limiter.ratio.value = 20;
|
|
64
|
+
limiter.attack.value = 0.003;
|
|
65
|
+
limiter.release.value = 0.25;
|
|
66
|
+
master.connect(limiter);
|
|
67
|
+
limiter.connect(opts.destination ?? this.ctx.destination);
|
|
68
|
+
this.masterGain = master;
|
|
69
|
+
this.limiter = limiter;
|
|
70
|
+
this.engine = new LayerEngine({
|
|
71
|
+
context: this.ctx,
|
|
72
|
+
destination: master,
|
|
73
|
+
loadBuffer: this.loadBuffer,
|
|
74
|
+
onLog: opts.onLog,
|
|
75
|
+
normalize: opts.normalize,
|
|
76
|
+
});
|
|
77
|
+
this.engineUnsub = this.engine.onChange(() => this.onEngineChange());
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Dispatch a single room frame. The full `play` / `layer.*` dialect; `room.code_rotated` and other
|
|
81
|
+
* transport-terminal frames stay with the host (they never reach here). An unrecognized type falls
|
|
82
|
+
* through to the `unhandled` hook.
|
|
83
|
+
*/
|
|
84
|
+
handleSignal(msg) {
|
|
85
|
+
switch (msg.type) {
|
|
86
|
+
case "play": {
|
|
87
|
+
const p = (msg.payload ?? {});
|
|
88
|
+
if (Array.isArray(p.tracks)) {
|
|
89
|
+
// Unified play (layer/mode/tracks) — serialize so a queue lands in wire order.
|
|
90
|
+
this.playChain = this.playChain
|
|
91
|
+
.then(() => this.onPlaySignal(p, msg))
|
|
92
|
+
.catch(() => this.log("· play failed"));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// Legacy flat play: a single one-shot (or spotify/link) on the layer.
|
|
96
|
+
void this.handleFlatPlay(p, typeof p.layer === "string" ? p.layer : "default", msg);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
case "layer.configure":
|
|
101
|
+
this.onLayerConfigure(msg.payload);
|
|
102
|
+
return;
|
|
103
|
+
case "layer.clear":
|
|
104
|
+
this.onLayerClear(msg.payload);
|
|
105
|
+
return;
|
|
106
|
+
case "layer.effects":
|
|
107
|
+
this.onLayerEffects(msg.payload);
|
|
108
|
+
return;
|
|
109
|
+
case "layer.position":
|
|
110
|
+
this.onLayerPosition(msg.payload);
|
|
111
|
+
return;
|
|
112
|
+
case "layer.query":
|
|
113
|
+
this.onLayerQuery(msg.from, msg.ack_id);
|
|
114
|
+
return;
|
|
115
|
+
case "layer.subscribe":
|
|
116
|
+
this.addSubscriber(msg.from);
|
|
117
|
+
return;
|
|
118
|
+
case "layer.unsubscribe":
|
|
119
|
+
this.removeSubscriber(msg.from);
|
|
120
|
+
return;
|
|
121
|
+
case "presence.leave":
|
|
122
|
+
this.removeSubscriber(msg.payload?.connection_id);
|
|
123
|
+
return;
|
|
124
|
+
default:
|
|
125
|
+
// Not part of the dialect — hand it to the host (its own message types), else log-and-skip
|
|
126
|
+
// (mirrors handleFlatPlay's unrecognized-intent breadcrumb).
|
|
127
|
+
if (!this.opts.unhandled?.(msg))
|
|
128
|
+
this.log(`· ignored: ${msg.type}`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Tear down the engine + the runtime's own timers. Leaves the AudioContext (the host created it)
|
|
133
|
+
* but disconnects the master chain this runtime built. Does not clear a shared subscriber set. */
|
|
134
|
+
dispose() {
|
|
135
|
+
this.disposed = true;
|
|
136
|
+
this.engineUnsub();
|
|
137
|
+
if (this.broadcastTimer != null)
|
|
138
|
+
clearTimeout(this.broadcastTimer);
|
|
139
|
+
if (this.heartbeatTimer != null)
|
|
140
|
+
clearInterval(this.heartbeatTimer);
|
|
141
|
+
this.engine.dispose();
|
|
142
|
+
this.disconnect(this.masterGain);
|
|
143
|
+
this.disconnect(this.limiter);
|
|
144
|
+
}
|
|
145
|
+
disconnect(node) {
|
|
146
|
+
try {
|
|
147
|
+
node.disconnect();
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// already disconnected
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ── Track resolution ────────────────────────────────────────────────────────────────────────────
|
|
154
|
+
// Decoded-buffer loader, cached by content URL — both the one-shot `play` path and the engine's
|
|
155
|
+
// queue playback share this one cache.
|
|
156
|
+
loadBuffer = async (url) => {
|
|
157
|
+
const cached = this.buffers.get(url);
|
|
158
|
+
if (cached)
|
|
159
|
+
return cached;
|
|
160
|
+
const res = await httpFetch(url);
|
|
161
|
+
if (!res.ok)
|
|
162
|
+
throw new Error(String(res.status));
|
|
163
|
+
const buf = await this.ctx.decodeAudioData(await res.arrayBuffer());
|
|
164
|
+
this.buffers.set(url, buf);
|
|
165
|
+
return buf;
|
|
166
|
+
};
|
|
167
|
+
// Single-asset metadata is public (content-by-id capability); used only for the asset_id-only play
|
|
168
|
+
// shape to recover a name label + facts (bpm / loudness). Content bytes stream from the derived URL
|
|
169
|
+
// regardless. Adapts the public `AssetPublicRead` shape (`title`/`filename`, no `content_url`) that
|
|
170
|
+
// `fetchAssetMeta` returns onto the `AssetLike` the play resolver reads — so an asset_id-only play
|
|
171
|
+
// keeps its title as the now-playing label (the server never sends `name`/`content_url`).
|
|
172
|
+
async fetchAsset(assetId) {
|
|
173
|
+
const meta = await fetchAssetMeta(this.opts.baseUrl ?? "", assetId);
|
|
174
|
+
if (!meta)
|
|
175
|
+
return null;
|
|
176
|
+
return {
|
|
177
|
+
id: assetId,
|
|
178
|
+
content_url: contentUrlFor(assetId),
|
|
179
|
+
name: meta.title ?? meta.filename,
|
|
180
|
+
facts: meta.facts,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// Resolve a batch of wire tracks to playable LayerTracks concurrently (the asset_id-only shape needs
|
|
184
|
+
// a metadata fetch); order is preserved so a queue lands in wire order.
|
|
185
|
+
async resolveWireTracks(wireTracks) {
|
|
186
|
+
const results = await Promise.all(wireTracks.map(async (wt) => {
|
|
187
|
+
const fetchId = wireTrackFetchId(wt);
|
|
188
|
+
const fetched = fetchId ? await this.fetchAsset(fetchId) : null;
|
|
189
|
+
return resolveWireTrack(wt, fetched);
|
|
190
|
+
}));
|
|
191
|
+
return results.filter((t) => t !== null);
|
|
192
|
+
}
|
|
193
|
+
// ── play ────────────────────────────────────────────────────────────────────────────────────────
|
|
194
|
+
// Resolve a bytes PlayCommand to a track and fire it as a one-shot on `layerId` via the engine
|
|
195
|
+
// (the single one-shot pipeline — delay/max-duration/fades/spatial all live in the engine).
|
|
196
|
+
async playBytesOneshot(cmd, layerId) {
|
|
197
|
+
const fetched = needsAssetFetch(cmd) && cmd.asset_id ? await this.fetchAsset(cmd.asset_id) : null;
|
|
198
|
+
if (this.disposed)
|
|
199
|
+
return; // torn down mid-fetch — don't touch the disposed engine
|
|
200
|
+
const resolved = resolvePlayback(cmd, fetched);
|
|
201
|
+
if (!resolved) {
|
|
202
|
+
this.log("· nothing to play (no content_url / asset)");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// carryTrackFields is the one source of truth for the per-sound fields (a PlayCommand is a
|
|
206
|
+
// WireTrack-compatible shape) — so a new field is a single edit, not two hand-lists.
|
|
207
|
+
this.engine.playOneshots(layerId, [
|
|
208
|
+
{
|
|
209
|
+
...fetchedLoudness(fetched),
|
|
210
|
+
...carryTrackFields(cmd),
|
|
211
|
+
url: resolved.url,
|
|
212
|
+
name: resolved.label,
|
|
213
|
+
},
|
|
214
|
+
]);
|
|
215
|
+
// Seed a host visualizer with the sound's name + (when known) tempo, so its bpm prior and
|
|
216
|
+
// now-playing label reflect it.
|
|
217
|
+
this.opts.onTrack?.(resolved.bpm != null ? { name: resolved.label, bpm: resolved.bpm } : { name: resolved.label });
|
|
218
|
+
this.log(`▶ [${layerId}] ${resolved.label}${cmd.delay_seconds ? ` +${cmd.delay_seconds}s` : ""}`);
|
|
219
|
+
}
|
|
220
|
+
// A legacy flat `play` payload (a single sound, no `tracks`): a one-shot on the layer, or a
|
|
221
|
+
// spotify/link intent handed to the host. Kept for existing hosts (e.g. the HA bridge).
|
|
222
|
+
async handleFlatPlay(cmd, layerId, original) {
|
|
223
|
+
const route = routePlayIntent(cmd);
|
|
224
|
+
if (route.kind === "bytes")
|
|
225
|
+
return this.playBytesOneshot(route.cmd, layerId);
|
|
226
|
+
// Non-bytes (spotify / link / none) live outside the runtime — hand the frame to the host.
|
|
227
|
+
if (this.opts.unhandled?.(original))
|
|
228
|
+
return;
|
|
229
|
+
this.log("· nothing to play (unrecognized play signal)");
|
|
230
|
+
}
|
|
231
|
+
// The unified `play` signal: `{ layer?, mode?, tracks[], transition? }`. `mode:"queue"` drives the
|
|
232
|
+
// layer's single-track queue; `mode:"oneshot"` (default) fires overlapping one-shots. A spotify
|
|
233
|
+
// track routes to the host in oneshot mode; in queue mode the engine stubs it (reserved).
|
|
234
|
+
async onPlaySignal(payload, original) {
|
|
235
|
+
const layerId = typeof payload.layer === "string" ? payload.layer : "default";
|
|
236
|
+
const resolved = await this.resolveWireTracks(Array.isArray(payload.tracks) ? payload.tracks : []);
|
|
237
|
+
if (this.disposed)
|
|
238
|
+
return; // torn down mid-resolution — don't touch the disposed engine
|
|
239
|
+
if (resolved.length === 0) {
|
|
240
|
+
this.log("· nothing to play (no playable tracks)");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (payload.mode === "queue") {
|
|
244
|
+
// Queue mode doesn't support spotify (reserved/stubbed) — the engine drops those, so only the
|
|
245
|
+
// bytes tracks actually queue. Log what really queued, not the raw batch.
|
|
246
|
+
const queued = resolved.filter((t) => !t.spotify_ref);
|
|
247
|
+
const transition = validateTransition(payload.transition ?? "now");
|
|
248
|
+
const mode = { repeat: payload.repeat === true, shuffle: payload.shuffle === true };
|
|
249
|
+
this.engine.enqueue(layerId, resolved, transition, mode);
|
|
250
|
+
const first = queued[0];
|
|
251
|
+
if (first) {
|
|
252
|
+
const name = first.name ?? first.url ?? "track";
|
|
253
|
+
this.opts.onTrack?.({ name });
|
|
254
|
+
const modes = [mode.shuffle && "shuffle", mode.repeat && "repeat"].filter(Boolean);
|
|
255
|
+
this.log(`▶ [${layerId}] ${name} (queue: ${transition}${modes.length ? `, ${modes.join("+")}` : ""})`);
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// oneshot: bytes overlap on the layer; spotify tracks fire through the host.
|
|
260
|
+
const bytes = resolved.filter((t) => !t.spotify_ref);
|
|
261
|
+
const firstByte = bytes[0];
|
|
262
|
+
if (firstByte) {
|
|
263
|
+
this.engine.playOneshots(layerId, bytes);
|
|
264
|
+
const name = firstByte.name ?? firstByte.url ?? "sound";
|
|
265
|
+
this.opts.onTrack?.({ name });
|
|
266
|
+
this.log(`▶ [${layerId}] ${name}${bytes.length > 1 ? ` +${bytes.length - 1}` : ""}`);
|
|
267
|
+
}
|
|
268
|
+
for (const t of resolved) {
|
|
269
|
+
if (!t.spotify_ref)
|
|
270
|
+
continue;
|
|
271
|
+
// Re-synthesize the flat-play `unhandled` contract: carry the resolved `layer` and the original
|
|
272
|
+
// frame's `from`/`ack_id` so the host has the same routing context it gets on the flat path.
|
|
273
|
+
const consumed = this.opts.unhandled?.({
|
|
274
|
+
type: "play",
|
|
275
|
+
payload: {
|
|
276
|
+
layer: layerId,
|
|
277
|
+
playback: { source: "spotify", ref: t.spotify_ref, mode: t.spotify_mode },
|
|
278
|
+
},
|
|
279
|
+
from: original.from,
|
|
280
|
+
ack_id: original.ack_id,
|
|
281
|
+
});
|
|
282
|
+
if (!consumed)
|
|
283
|
+
this.log(`· spotify track in layer not yet supported: ${t.name ?? t.spotify_ref}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// ── layer.* dialect ─────────────────────────────────────────────────────────────────────────────
|
|
287
|
+
onLayerConfigure(payload) {
|
|
288
|
+
const raw = Array.isArray(payload?.layers) ? payload.layers : [];
|
|
289
|
+
// Be tolerant at the wire boundary: a `null` / scalar / array entry (or one with no id) would
|
|
290
|
+
// throw inside `engine.configure` — and this runs from a socket handler, where a throw takes the
|
|
291
|
+
// whole dispatch down. Drop what can't be a layer, and coerce a non-string id (mirrors how the
|
|
292
|
+
// rest of the runtime decodes hand-rolled payloads).
|
|
293
|
+
const layers = raw
|
|
294
|
+
.filter((spec) => typeof spec === "object" && spec !== null && !Array.isArray(spec) && spec.id != null)
|
|
295
|
+
.map((spec) => ({ ...spec, id: String(spec.id) }));
|
|
296
|
+
this.engine.configure(layers);
|
|
297
|
+
}
|
|
298
|
+
onLayerClear(payload) {
|
|
299
|
+
const layerId = typeof payload?.layer === "string" ? payload.layer : "default";
|
|
300
|
+
const fade = typeof payload?.fade_out_ms === "number" ? payload.fade_out_ms : undefined;
|
|
301
|
+
// "*" clears every layer at once (a "stop all audio"); otherwise just the named layer.
|
|
302
|
+
if (layerId === "*") {
|
|
303
|
+
this.engine.clearAll(fade);
|
|
304
|
+
this.log("· cleared all layers");
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
this.engine.clear(layerId, fade);
|
|
308
|
+
this.log(`· cleared [${layerId}]`);
|
|
309
|
+
}
|
|
310
|
+
onLayerEffects(payload) {
|
|
311
|
+
const layerId = typeof payload?.layer === "string" ? payload.layer : "default";
|
|
312
|
+
const effects = payload?.effects ?? {};
|
|
313
|
+
const fade = typeof payload?.fade_ms === "number" ? payload.fade_ms : undefined;
|
|
314
|
+
// "*" applies to every EXISTING layer at once (mirrors layer.clear "*"); no auto-vivify.
|
|
315
|
+
if (layerId === "*") {
|
|
316
|
+
this.engine.setEffectsAll(effects, fade);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.engine.setEffects(layerId, effects, fade);
|
|
320
|
+
}
|
|
321
|
+
// Ramped whole-layer HRTF positioning. The panner is created lazily on first use (an unpositioned
|
|
322
|
+
// layer keeps today's graph); `distance`, when given, opts into distance attenuation. No "*".
|
|
323
|
+
onLayerPosition(payload) {
|
|
324
|
+
const layerId = typeof payload?.layer === "string" ? payload.layer : "default";
|
|
325
|
+
if (layerId === "*") {
|
|
326
|
+
this.log(`· layer.position has no "*" wildcard — ignored`);
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : 0);
|
|
330
|
+
this.engine.setPosition(layerId, num(payload?.x), num(payload?.y), num(payload?.z), typeof payload?.fade_ms === "number" ? payload.fade_ms : undefined, payload?.distance);
|
|
331
|
+
}
|
|
332
|
+
// Pull: reply the exact snapshot straight back to the requester (`to=from`, `reply_to=ack_id`).
|
|
333
|
+
onLayerQuery(from, ackId) {
|
|
334
|
+
if (!from)
|
|
335
|
+
return;
|
|
336
|
+
const layers = this.engine.snapshot();
|
|
337
|
+
this.opts.send("layer.query", { layers }, { to: from, replyTo: ackId, ephemeral: true });
|
|
338
|
+
}
|
|
339
|
+
// ── Subscriber fan-out ──────────────────────────────────────────────────────────────────────────
|
|
340
|
+
// Push the current snapshot to every subscriber (one broadcast, server fans it out). Accepts a
|
|
341
|
+
// pre-computed snapshot so the heartbeat doesn't have to snapshot twice.
|
|
342
|
+
broadcastState(layers) {
|
|
343
|
+
if (this.subscribers.size === 0)
|
|
344
|
+
return;
|
|
345
|
+
const snap = layers ?? this.engine.snapshot();
|
|
346
|
+
this.opts.send("layer.state", { layers: snap }, { ephemeral: true });
|
|
347
|
+
}
|
|
348
|
+
// Coalesce a burst of engine changes into one broadcast — the on-change half of the subscribe
|
|
349
|
+
// contract; the heartbeat below covers a playing layer's elapsed/remaining drift.
|
|
350
|
+
scheduleBroadcast() {
|
|
351
|
+
if (this.subscribers.size === 0)
|
|
352
|
+
return;
|
|
353
|
+
if (this.broadcastTimer != null)
|
|
354
|
+
return;
|
|
355
|
+
this.broadcastTimer = setTimeout(() => {
|
|
356
|
+
this.broadcastTimer = null;
|
|
357
|
+
this.broadcastState();
|
|
358
|
+
}, BROADCAST_DEBOUNCE_MS);
|
|
359
|
+
}
|
|
360
|
+
addSubscriber(from) {
|
|
361
|
+
if (!from)
|
|
362
|
+
return;
|
|
363
|
+
this.subscribers.add(from);
|
|
364
|
+
this.broadcastState(); // immediate first push so a fresh subscriber isn't blind until the next change
|
|
365
|
+
// A subscriber joining mid-playback starts the heartbeat (it was gated off with no subscribers).
|
|
366
|
+
this.updateHeartbeat(this.engine.snapshot());
|
|
367
|
+
}
|
|
368
|
+
removeSubscriber(from) {
|
|
369
|
+
if (!from)
|
|
370
|
+
return;
|
|
371
|
+
this.subscribers.delete(from);
|
|
372
|
+
// The last subscriber leaving stops the heartbeat (nothing left to broadcast to).
|
|
373
|
+
this.updateHeartbeat(this.engine.snapshot());
|
|
374
|
+
}
|
|
375
|
+
// The engine only notifies on discrete changes, so a playing layer's elapsed/remaining clock would
|
|
376
|
+
// freeze between them — while anything plays AND someone's subscribed, re-broadcast once a second.
|
|
377
|
+
// Gated on subscribers too so an unwatched Player doesn't run a pointless timer; addSubscriber /
|
|
378
|
+
// removeSubscriber re-evaluate it so joining/leaving mid-playback starts/stops it.
|
|
379
|
+
updateHeartbeat(snap) {
|
|
380
|
+
const playing = this.subscribers.size > 0 && Object.values(snap).some((l) => l.playing);
|
|
381
|
+
if (playing && this.heartbeatTimer == null) {
|
|
382
|
+
this.heartbeatTimer = setInterval(() => {
|
|
383
|
+
this.broadcastState();
|
|
384
|
+
}, HEARTBEAT_MS);
|
|
385
|
+
}
|
|
386
|
+
else if (!playing && this.heartbeatTimer != null) {
|
|
387
|
+
clearInterval(this.heartbeatTimer);
|
|
388
|
+
this.heartbeatTimer = null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
onEngineChange() {
|
|
392
|
+
// One snapshot feeds every consumer of this change (heartbeat gate + the host's onSnapshot).
|
|
393
|
+
const snap = this.engine.snapshot();
|
|
394
|
+
this.scheduleBroadcast();
|
|
395
|
+
this.updateHeartbeat(snap);
|
|
396
|
+
this.opts.onSnapshot?.(snap);
|
|
397
|
+
}
|
|
398
|
+
log(text) {
|
|
399
|
+
this.opts.onLog?.(text);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/player/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { type ReactNode } from "react";
|
|
11
11
|
import type { Atrium } from "../atrium.js";
|
|
12
12
|
import type { User } from "../types.js";
|
|
13
|
+
export { type RoomConnection, type RoomStatus, type UseRoomConnectionOptions, useRoomConnection, } from "./use-room-connection.js";
|
|
13
14
|
export interface AtriumProviderProps {
|
|
14
15
|
atrium: Atrium;
|
|
15
16
|
children: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAiB,KAAK,SAAS,EAAmC,MAAM,OAAO,CAAA;AACtF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAiB,KAAK,SAAS,EAAmC,MAAM,OAAO,CAAA;AACtF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,wBAAwB,EAC7B,iBAAiB,GAClB,MAAM,0BAA0B,CAAA;AAUjC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,mBAAmB,GAAG,SAAS,CAoCnF;AAUD,uDAAuD;AACvD,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,oGAAoG;AACpG,wBAAgB,OAAO,IAAI,IAAI,GAAG,IAAI,GAAG,SAAS,CAEjD;AAED,6FAA6F;AAC7F,wBAAgB,YAAY,IAAI,KAAK,GAAG,IAAI,CAE3C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAA;IACnB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAUnF"}
|
package/dist/react/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
|
9
9
|
* flight, ``null`` when signed out, and a ``User`` when signed in.
|
|
10
10
|
*/
|
|
11
11
|
import { createContext, useContext, useEffect, useState } from "react";
|
|
12
|
+
export { useRoomConnection, } from "./use-room-connection.js";
|
|
12
13
|
const AtriumContext = createContext(null);
|
|
13
14
|
/** Provides the ``Atrium`` client and the live user to the ``useAtrium`` / ``useUser`` hooks. */
|
|
14
15
|
export function AtriumProvider({ atrium, children }) {
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAkB,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ``useRoomConnection`` — the connect/reconnect skeleton shared by the browser tools that consume a
|
|
3
|
+
* room by connect code (Dashboard, Viewer).
|
|
4
|
+
*
|
|
5
|
+
* It owns exactly the transport lifecycle: ``joinRoom(code)`` → classify a join failure
|
|
6
|
+
* (``APIError`` 4xx = a bad code, anything else = unreachable) → register a terminal ``onEnd``
|
|
7
|
+
* (the typed reason becomes the status) → ``await room.connect()`` (a non-terminal failure =
|
|
8
|
+
* unreachable) → remount-safe cleanup (a ``cancelled`` flag guards every late setState, and
|
|
9
|
+
* ``room.close()`` tears the handle down). App-specific signal handling stays outside: pass
|
|
10
|
+
* ``onRoom`` to register handlers on the room the moment it exists — **before** ``connect()`` runs,
|
|
11
|
+
* so a durable-signal replay isn't missed — and return an optional cleanup from it.
|
|
12
|
+
*/
|
|
13
|
+
import { type Room, type RoomEndReason } from "../rooms/index.js";
|
|
14
|
+
/**
|
|
15
|
+
* The transport-level status, independent of anything the app puts on the wire. ``"connecting"``
|
|
16
|
+
* until the room is joined and connected; ``"connected"`` once in; ``"unreachable"`` for a join or
|
|
17
|
+
* connect failure with no typed end; the ``RoomEndReason`` variants (``"bad-code"`` |
|
|
18
|
+
* ``"code-rotated"`` | ``"room-gone"``) for a typed terminal end (a bad code at join maps to
|
|
19
|
+
* ``"bad-code"`` too). Callers map this onto their own richer view model.
|
|
20
|
+
*/
|
|
21
|
+
export type RoomStatus = "connecting" | "connected" | "unreachable" | RoomEndReason;
|
|
22
|
+
export interface UseRoomConnectionOptions {
|
|
23
|
+
/** Presence identity for the join (e.g. ``"viewer:web"``). */
|
|
24
|
+
identity: string;
|
|
25
|
+
/** Room resolution root. Defaults to ``location.origin``. */
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Called once the room handle exists, **before** ``connect()`` runs — the point to register
|
|
29
|
+
* signal handlers (``room.on`` / ``room.onAny``) so a replayed durable signal isn't missed. May
|
|
30
|
+
* return a cleanup, run on unmount / reconnect before the handle is closed.
|
|
31
|
+
*/
|
|
32
|
+
onRoom?: (room: Room) => void | (() => void);
|
|
33
|
+
}
|
|
34
|
+
export interface RoomConnection {
|
|
35
|
+
status: RoomStatus;
|
|
36
|
+
/** The joined room, or ``null`` before it exists / after teardown. */
|
|
37
|
+
room: Room | null;
|
|
38
|
+
}
|
|
39
|
+
export declare function useRoomConnection(code: string, opts: UseRoomConnectionOptions): RoomConnection;
|
|
40
|
+
//# sourceMappingURL=use-room-connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-room-connection.d.ts","sourceRoot":"","sources":["../../src/react/use-room-connection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAY,KAAK,IAAI,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE3E;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,WAAW,GAAG,aAAa,GAAG,aAAa,CAAA;AAEnF,MAAM,WAAW,wBAAwB;IACvC,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAA;IAChB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IAEH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAA;IAClB,sEAAsE;IACtE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;CAClB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,GAAG,cAAc,CAoE9F"}
|