@miadi/stateloom-web 0.3.0 → 0.3.3
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/package.json +1 -1
- package/server/.next/BUILD_ID +1 -1
- package/server/.next/build-manifest.json +2 -2
- package/server/.next/server/app/_global-error.html +2 -2
- package/server/.next/server/app/_global-error.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/server/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/server/.next/server/app/_not-found.html +1 -1
- package/server/.next/server/app/_not-found.rsc +2 -2
- package/server/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/server/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/server/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/server/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/server/.next/server/app/api/docs/route.js.nft.json +1 -1
- package/server/.next/server/app/api/file/route.js.nft.json +1 -1
- package/server/.next/server/app/api/marks/route.js.nft.json +1 -1
- package/server/.next/server/app/api/rispec/route.js.nft.json +1 -1
- package/server/.next/server/app/api/watch/route.js.nft.json +1 -1
- package/server/.next/server/app/index.html +1 -1
- package/server/.next/server/app/index.rsc +3 -3
- package/server/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/server/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/server/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/server/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/server/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/server/.next/server/app/page.js.nft.json +1 -1
- package/server/.next/server/app/page_client-reference-manifest.js +1 -1
- package/server/.next/server/chunks/ssr/{[root-of-the-server]__9e661f75._.js → [root-of-the-server]__a593e8b6._.js} +1 -1
- package/server/.next/server/chunks/ssr/[root-of-the-server]__edbb995c._.js +6 -6
- package/server/.next/server/pages/404.html +1 -1
- package/server/.next/server/pages/500.html +2 -2
- package/server/.next/static/chunks/59a1ec89a1dc7e34.js +14 -0
- package/server/.next/static/chunks/{0d24f3e3dbdef25e.css → 8f45e56f40c207bf.css} +2 -2
- package/server/package-lock.json +2 -2
- package/server/package.json +1 -1
- package/server/src/app/globals.css +20 -0
- package/server/src/components/stage/CompanionLayer.tsx +10 -1
- package/server/src/components/stage/StageLayer.tsx +84 -8
- package/server/src/components/stage/StageProvider.tsx +4 -1
- package/server/src/components/stage/useEpisodePresence.ts +71 -0
- package/server/stateloom-web.json +2 -2
- package/server/tsconfig.tsbuildinfo +1 -1
- package/server/.next/static/chunks/f03f883bab5194ab.js +0 -14
- /package/server/.next/static/{HNER2j3kS6-hB98CL_HRG → CZ4wRF1efsHnxjkcKHITP}/_buildManifest.js +0 -0
- /package/server/.next/static/{HNER2j3kS6-hB98CL_HRG → CZ4wRF1efsHnxjkcKHITP}/_clientMiddlewareManifest.json +0 -0
- /package/server/.next/static/{HNER2j3kS6-hB98CL_HRG → CZ4wRF1efsHnxjkcKHITP}/_ssgManifest.js +0 -0
|
@@ -54,6 +54,8 @@ export interface StageValue {
|
|
|
54
54
|
shown: StageShown | null;
|
|
55
55
|
/** Play a tour from this page in its own voice — a companion's reply (Spec 84). */
|
|
56
56
|
present: UseStage["present"];
|
|
57
|
+
/** A companion answers behind `/api/companion` (Spec 84): the mark cards say who answers. */
|
|
58
|
+
companion: boolean;
|
|
57
59
|
pause: () => void;
|
|
58
60
|
stop: () => void;
|
|
59
61
|
}
|
|
@@ -220,10 +222,11 @@ export default function StageProvider({ children }: { children: ReactNode }) {
|
|
|
220
222
|
},
|
|
221
223
|
shown: stage.shown,
|
|
222
224
|
present: stage.present,
|
|
225
|
+
companion: config.companion,
|
|
223
226
|
pause: stage.pause,
|
|
224
227
|
stop: stage.stop,
|
|
225
228
|
}),
|
|
226
|
-
[config.stage, stage.status, stage.shown, stage.sendMarks, stage.present, stage.pause, stage.stop, lit, author, listener, answerNonce, report],
|
|
229
|
+
[config.stage, config.companion, stage.status, stage.shown, stage.sendMarks, stage.present, stage.pause, stage.stop, lit, author, listener, answerNonce, report],
|
|
227
230
|
);
|
|
228
231
|
|
|
229
232
|
return (
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a Mia seat listens in the episode a drawing belongs to (Spec 83, Spec 84),
|
|
5
|
+
* for the line the mark cards show under what a person writes and reads.
|
|
6
|
+
*
|
|
7
|
+
* Read from the companion's thread through this designer's proxy
|
|
8
|
+
* (`/api/companion/thread?episode=`), the same presence the Talk dock shows: listening,
|
|
9
|
+
* answering, answered, away. Polled every 10 s while a mark card is open and the page is
|
|
10
|
+
* visible; what was last read is kept while the cards are closed, so a card opens with
|
|
11
|
+
* its line. Null when there is no episode, before the first answer, and when the
|
|
12
|
+
* companion does not answer.
|
|
13
|
+
*/
|
|
14
|
+
import { useEffect, useState } from "react";
|
|
15
|
+
import type { CompanionPresence } from "@miadi/stateloom-react";
|
|
16
|
+
|
|
17
|
+
export type PresenceState = CompanionPresence["state"];
|
|
18
|
+
|
|
19
|
+
const EVERY_MS = 10_000;
|
|
20
|
+
|
|
21
|
+
export function useEpisodePresence(episode: string | null, active: boolean): PresenceState | null {
|
|
22
|
+
const [seen, setSeen] = useState<{ episode: string; state: PresenceState | null } | null>(null);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!episode || !active) return;
|
|
26
|
+
let live = true;
|
|
27
|
+
const read = async (): Promise<void> => {
|
|
28
|
+
if (typeof document !== "undefined" && document.visibilityState !== "visible") return;
|
|
29
|
+
try {
|
|
30
|
+
const r = await fetch(`/api/companion/thread?episode=${encodeURIComponent(episode)}`, { cache: "no-store" });
|
|
31
|
+
const body = (await r.json()) as { success?: boolean; presence?: CompanionPresence | null };
|
|
32
|
+
if (live) setSeen({ episode, state: body.success ? (body.presence?.state ?? null) : null });
|
|
33
|
+
} catch {
|
|
34
|
+
if (live) setSeen({ episode, state: null });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
void read();
|
|
38
|
+
const t = setInterval(() => void read(), EVERY_MS);
|
|
39
|
+
return () => {
|
|
40
|
+
live = false;
|
|
41
|
+
clearInterval(t);
|
|
42
|
+
};
|
|
43
|
+
}, [episode, active]);
|
|
44
|
+
|
|
45
|
+
return seen && seen.episode === episode ? seen.state : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** "2026-10-19-episode-412-the-loom" → 412. */
|
|
49
|
+
export function episodeNumber(episode: string): number | null {
|
|
50
|
+
const m = episode.match(/-episode-(\d+)-/);
|
|
51
|
+
return m ? Number(m[1]) : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The line under the mark cards: who answers a mark left on a drawing of `episode`. */
|
|
55
|
+
export function presenceLine(state: PresenceState | null, episode: string | null): string | null {
|
|
56
|
+
if (!state || !episode) return null;
|
|
57
|
+
const n = episodeNumber(episode);
|
|
58
|
+
const where = n === null ? "this episode" : `episode ${n}`;
|
|
59
|
+
switch (state) {
|
|
60
|
+
case "listening":
|
|
61
|
+
return `Mia is listening in ${where}: she answers here`;
|
|
62
|
+
case "answering":
|
|
63
|
+
return "Mia is answering";
|
|
64
|
+
case "answered":
|
|
65
|
+
return `Mia has answered in ${where}: your mark waits until she listens again`;
|
|
66
|
+
case "away":
|
|
67
|
+
return `No seat is listening in ${where}: your mark waits until one does`;
|
|
68
|
+
default:
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|