@graphlearning/shell 0.3.0 → 0.5.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/dist/index.js CHANGED
@@ -1,173 +1,186 @@
1
- import { jsx as e, jsxs as i, Fragment as I } from "react/jsx-runtime";
2
- import { useRef as L, useState as b, useLayoutEffect as D, useEffect as N, useMemo as E, useCallback as z } from "react";
3
- import { SceneView as S } from "@graphlearning/flow";
4
- import { Home as G, Volume2 as K, VolumeX as V, ChevronLeft as R, ChevronRight as W } from "lucide-react";
5
- import X from "react-markdown";
6
- import F from "remark-gfm";
7
- function j(a, c) {
8
- return `${a.id}-${c.id}`;
1
+ import { jsx as e, jsxs as d, Fragment as M } from "react/jsx-runtime";
2
+ import { useRef as S, useState as x, useLayoutEffect as q, useEffect as N, useCallback as T, useMemo as C } from "react";
3
+ import { SceneView as H } from "@graphlearning/flow";
4
+ import { Home as W, Volume2 as X, VolumeX as O, Moon as Y, Sun as Z, ChevronLeft as J, ChevronRight as Q } from "lucide-react";
5
+ import U from "react-markdown";
6
+ import ee from "remark-gfm";
7
+ function K(n, r) {
8
+ return `${n.id}-${r.id}`;
9
9
  }
10
- function P(a) {
11
- return a.sections.map((c) => ({ section: c, slug: j(a, c) }));
10
+ function z(n) {
11
+ return n.sections.map((r) => ({ section: r, slug: K(n, r) }));
12
12
  }
13
- const B = 806;
14
- function O() {
15
- const a = L(null), [c, s] = b(1);
16
- return D(() => {
17
- const l = a.current;
18
- if (!l) return;
19
- const h = () => s(l.clientWidth / B);
20
- h();
21
- const f = new ResizeObserver(h);
22
- return f.observe(l), () => f.disconnect();
23
- }, []), { ref: a, zoom: c };
13
+ const F = 806;
14
+ function te() {
15
+ const n = S(null), [r, s] = x(1);
16
+ return q(() => {
17
+ const o = n.current;
18
+ if (!o) return;
19
+ const l = () => s(o.clientWidth / F);
20
+ l();
21
+ const u = new ResizeObserver(l);
22
+ return u.observe(o), () => u.disconnect();
23
+ }, []), { ref: n, zoom: r };
24
24
  }
25
- function Z({ slide: a, open: c }) {
26
- const { ref: s, zoom: l } = O();
27
- return /* @__PURE__ */ e("aside", { ref: s, className: `slide-panel${c ? " slide-panel--open" : ""}`, children: /* @__PURE__ */ e("div", { className: "slide-panel__scaler", style: { width: B, zoom: l }, children: /* @__PURE__ */ e(X, { remarkPlugins: [F], children: a }) }) });
25
+ function ne({ slide: n, open: r }) {
26
+ const { ref: s, zoom: o } = te();
27
+ return /* @__PURE__ */ e("aside", { ref: s, className: `slide-panel${r ? " slide-panel--open" : ""}`, children: /* @__PURE__ */ e("div", { className: "slide-panel__scaler", style: { width: F, zoom: o }, children: /* @__PURE__ */ e(U, { remarkPlugins: [ee], children: n }) }) });
28
28
  }
29
- function q({
30
- section: a,
31
- getScene: c,
29
+ function ae({
30
+ section: n,
31
+ getScene: r,
32
32
  capture: s = !1,
33
- eyebrow: l,
34
- index: h = 0,
35
- total: f = 0,
36
- onHome: o,
37
- onPrev: _,
38
- onNext: u,
39
- narrating: n = !1,
40
- onToggleNarration: p
33
+ eyebrow: o,
34
+ index: l = 0,
35
+ total: u = 0,
36
+ onHome: m,
37
+ onPrev: c,
38
+ onNext: v,
39
+ narrating: t = !1,
40
+ onToggleNarration: w,
41
+ theme: k = "dark",
42
+ onToggleTheme: h
41
43
  }) {
42
- const [d, m] = b(!1), v = c(a.scene);
43
- return v ? /* @__PURE__ */ i("div", { className: "stage stage--section", children: [
44
- /* @__PURE__ */ i("header", { className: "reel-head", children: [
45
- l && /* @__PURE__ */ e("button", { className: "reel-head__eyebrow", onClick: o, "aria-label": "Back to catalog (Esc)", title: "Back to catalog (Esc)", children: l }),
46
- /* @__PURE__ */ e("h1", { className: "reel-head__title", children: a.title })
44
+ const [g, f] = x(!1), p = r(n.scene);
45
+ return p ? /* @__PURE__ */ d("div", { className: "stage stage--section", children: [
46
+ /* @__PURE__ */ d("header", { className: "reel-head", children: [
47
+ o && /* @__PURE__ */ e("button", { className: "reel-head__eyebrow", onClick: m, "aria-label": "Back to catalog (Esc)", title: "Back to catalog (Esc)", children: o }),
48
+ /* @__PURE__ */ e("h1", { className: "reel-head__title", children: n.title })
47
49
  ] }),
48
- !s && /* @__PURE__ */ i("footer", { className: "reel-foot reel-foot--controls", children: [
49
- /* @__PURE__ */ i("span", { className: "reel-foot__grp", children: [
50
- /* @__PURE__ */ e("button", { className: "reel-foot__ctrl", onClick: o, "aria-label": "Back to catalog (Esc)", title: "Back to catalog (Esc)", children: /* @__PURE__ */ e(G, { size: 17 }) }),
50
+ !s && /* @__PURE__ */ d("footer", { className: "reel-foot reel-foot--controls", children: [
51
+ /* @__PURE__ */ d("span", { className: "reel-foot__grp", children: [
52
+ /* @__PURE__ */ e("button", { className: "reel-foot__ctrl", onClick: m, "aria-label": "Back to catalog (Esc)", title: "Back to catalog (Esc)", children: /* @__PURE__ */ e(W, { size: 17 }) }),
51
53
  /* @__PURE__ */ e(
52
54
  "button",
53
55
  {
54
56
  className: "reel-foot__ctrl",
55
- onClick: p,
56
- "aria-label": n ? "Pause narration (Space)" : "Play narration (Space)",
57
- title: n ? "Pause narration (Space)" : "Play narration (Space)",
58
- children: n ? /* @__PURE__ */ e(K, { size: 18 }) : /* @__PURE__ */ e(V, { size: 18 })
57
+ onClick: w,
58
+ "aria-label": t ? "Pause narration (Space)" : "Play narration (Space)",
59
+ title: t ? "Pause narration (Space)" : "Play narration (Space)",
60
+ children: t ? /* @__PURE__ */ e(X, { size: 18 }) : /* @__PURE__ */ e(O, { size: 18 })
61
+ }
62
+ ),
63
+ h && /* @__PURE__ */ e(
64
+ "button",
65
+ {
66
+ className: "reel-foot__ctrl",
67
+ onClick: h,
68
+ "aria-label": k === "dark" ? "Dark theme. Switch to light theme." : "Light theme. Switch to dark theme.",
69
+ title: k === "dark" ? "Dark theme. Switch to light theme." : "Light theme. Switch to dark theme.",
70
+ children: k === "dark" ? /* @__PURE__ */ e(Y, { size: 17 }) : /* @__PURE__ */ e(Z, { size: 17 })
59
71
  }
60
72
  )
61
73
  ] }),
62
- /* @__PURE__ */ i("span", { className: "reel-foot__nav", children: [
63
- /* @__PURE__ */ e("button", { className: "reel-foot__ctrl", onClick: _, "aria-label": "Previous section (←)", title: "Previous section (←)", children: /* @__PURE__ */ e(R, { size: 19 }) }),
64
- /* @__PURE__ */ i("span", { className: "reel-foot__count reel-foot__count--live", children: [
65
- h + 1,
74
+ /* @__PURE__ */ d("span", { className: "reel-foot__nav", children: [
75
+ /* @__PURE__ */ e("button", { className: "reel-foot__ctrl", onClick: c, "aria-label": "Previous section (←)", title: "Previous section (←)", children: /* @__PURE__ */ e(J, { size: 19 }) }),
76
+ /* @__PURE__ */ d("span", { className: "reel-foot__count reel-foot__count--live", children: [
77
+ l + 1,
66
78
  " / ",
67
- f
79
+ u
68
80
  ] }),
69
- /* @__PURE__ */ e("button", { className: "reel-foot__ctrl", onClick: u, "aria-label": "Next section (→)", title: "Next section (→)", children: /* @__PURE__ */ e(W, { size: 19 }) })
81
+ /* @__PURE__ */ e("button", { className: "reel-foot__ctrl", onClick: v, "aria-label": "Next section (→)", title: "Next section (→)", children: /* @__PURE__ */ e(Q, { size: 19 }) })
70
82
  ] })
71
83
  ] }),
72
- /* @__PURE__ */ e("div", { className: "scene-area", children: /* @__PURE__ */ e(S, { scene: v, focusId: a.focus }) }),
84
+ /* @__PURE__ */ e("div", { className: "scene-area", children: /* @__PURE__ */ e(H, { scene: p, focusId: n.focus, theme: k }) }),
73
85
  !s && /* @__PURE__ */ e(
74
86
  "button",
75
87
  {
76
88
  className: "slide-toggle",
77
- onClick: () => m((w) => !w),
78
- "aria-label": d ? "Hide slide" : "Show slide",
79
- children: d ? "›" : "‹"
89
+ onClick: () => f((b) => !b),
90
+ "aria-label": g ? "Hide slide" : "Show slide",
91
+ children: g ? "›" : "‹"
80
92
  }
81
93
  ),
82
- /* @__PURE__ */ e(Z, { slide: a.slide, open: d })
83
- ] }) : /* @__PURE__ */ i("div", { className: "stage stage--missing", children: [
94
+ /* @__PURE__ */ e(ne, { slide: n.slide, open: g })
95
+ ] }) : /* @__PURE__ */ d("div", { className: "stage stage--missing", children: [
84
96
  "no scene: ",
85
- a.scene
97
+ n.scene
86
98
  ] });
87
99
  }
88
- const J = [
100
+ const se = [
89
101
  { id: "courses", label: "Courses" },
90
102
  { id: "labs", label: "Labs" },
91
103
  { id: "coach", label: "Coach" }
92
104
  ];
93
- function Q() {
94
- return /* @__PURE__ */ i("svg", { className: "site__logo", width: "28", height: "28", viewBox: "0 0 512 512", "aria-hidden": "true", children: [
105
+ function re() {
106
+ return /* @__PURE__ */ d("svg", { className: "site__logo", width: "28", height: "28", viewBox: "0 0 512 512", "aria-hidden": "true", children: [
95
107
  /* @__PURE__ */ e("rect", { width: "512", height: "512", rx: "80", fill: "#e8804f" }),
96
- /* @__PURE__ */ i("g", { fill: "none", stroke: "#ffffff", strokeWidth: "58", strokeLinecap: "butt", strokeLinejoin: "miter", children: [
108
+ /* @__PURE__ */ d("g", { fill: "none", stroke: "#ffffff", strokeWidth: "58", strokeLinecap: "butt", strokeLinejoin: "miter", children: [
97
109
  /* @__PURE__ */ e("path", { d: "M 373 200 A 130 130 0 1 0 373 312" }),
98
110
  /* @__PURE__ */ e("path", { d: "M 283 312 L 373 312 L 373 410" })
99
111
  ] })
100
112
  ] });
101
113
  }
102
- function Y({
103
- kind: a = "courses",
104
- nav: c = J,
114
+ function oe({
115
+ kind: n = "courses",
116
+ nav: r = se,
105
117
  home: s = "/",
106
- actions: l
118
+ actions: o
107
119
  }) {
108
- return /* @__PURE__ */ e("header", { className: "site", children: /* @__PURE__ */ i("div", { className: "site__inner", children: [
109
- /* @__PURE__ */ i("a", { className: "site__brand", href: s, children: [
110
- /* @__PURE__ */ e(Q, {}),
120
+ return /* @__PURE__ */ e("header", { className: "site", children: /* @__PURE__ */ d("div", { className: "site__inner", children: [
121
+ /* @__PURE__ */ d("a", { className: "site__brand", href: s, children: [
122
+ /* @__PURE__ */ e(re, {}),
111
123
  /* @__PURE__ */ e("span", { className: "site__name", children: "GraphL" })
112
124
  ] }),
113
- /* @__PURE__ */ e("nav", { className: "site__nav", "aria-label": "Sections", children: c.map((h) => /* @__PURE__ */ e(
125
+ /* @__PURE__ */ e("nav", { className: "site__nav", "aria-label": "Sections", children: r.map((l) => /* @__PURE__ */ e(
114
126
  "a",
115
127
  {
116
128
  className: "site__link",
117
- href: `${s}#${h.id}`,
118
- ...h.id === a ? { "aria-current": "page" } : {},
119
- children: h.label
129
+ href: `${s}#${l.id}`,
130
+ ...l.id === n ? { "aria-current": "page" } : {},
131
+ children: l.label
120
132
  },
121
- h.id
133
+ l.id
122
134
  )) }),
123
- /* @__PURE__ */ e("div", { className: "site__actions", children: l })
135
+ /* @__PURE__ */ e("div", { className: "site__actions", children: o })
124
136
  ] }) });
125
137
  }
126
- function U({
127
- courses: a,
128
- subject: c,
129
- kind: s
138
+ function ie({
139
+ courses: n,
140
+ subject: r,
141
+ kind: s,
142
+ actions: o
130
143
  }) {
131
- const [l, h] = b(() => /* @__PURE__ */ new Set()), f = (o) => h((_) => {
132
- const u = new Set(_);
133
- return u.has(o) ? u.delete(o) : u.add(o), u;
144
+ const [l, u] = x(() => /* @__PURE__ */ new Set()), m = (c) => u((v) => {
145
+ const t = new Set(v);
146
+ return t.has(c) ? t.delete(c) : t.add(c), t;
134
147
  });
135
148
  return (
136
149
  // The site bar is a SIBLING of .idx, not a child: it is full-bleed (its rule spans the
137
150
  // viewport) while .idx is a 940px centred column. Both use the same width and gutters, so the
138
151
  // brand lines up with the card edge below it.
139
- /* @__PURE__ */ i(I, { children: [
140
- /* @__PURE__ */ e(Y, { kind: s }),
141
- /* @__PURE__ */ i("div", { className: "idx", children: [
142
- /* @__PURE__ */ e("header", { className: "idx__head", children: /* @__PURE__ */ e("h1", { className: "idx__subject", children: c }) }),
143
- a.length === 0 ? /* @__PURE__ */ e("p", { className: "idx__empty", children: "No courses authored yet — the app shell is ready." }) : /* @__PURE__ */ e("ol", { className: "idx__grid", children: a.map((o, _) => {
144
- const u = P(o), n = l.has(o.id);
145
- return /* @__PURE__ */ i("li", { className: `idx-card${n ? " idx-card--open" : ""}`, children: [
146
- /* @__PURE__ */ i(
152
+ /* @__PURE__ */ d(M, { children: [
153
+ /* @__PURE__ */ e(oe, { kind: s, actions: o }),
154
+ /* @__PURE__ */ d("div", { className: "idx", children: [
155
+ /* @__PURE__ */ e("header", { className: "idx__head", children: /* @__PURE__ */ e("h1", { className: "idx__subject", children: r }) }),
156
+ n.length === 0 ? /* @__PURE__ */ e("p", { className: "idx__empty", children: "No courses authored yet — the app shell is ready." }) : /* @__PURE__ */ e("ol", { className: "idx__grid", children: n.map((c, v) => {
157
+ const t = z(c), w = l.has(c.id);
158
+ return /* @__PURE__ */ d("li", { className: `idx-card${w ? " idx-card--open" : ""}`, children: [
159
+ /* @__PURE__ */ d(
147
160
  "button",
148
161
  {
149
162
  className: "idx-card__head",
150
- onClick: () => f(o.id),
151
- "aria-expanded": n,
163
+ onClick: () => m(c.id),
164
+ "aria-expanded": w,
152
165
  children: [
153
- /* @__PURE__ */ e("span", { className: "idx-card__num", children: String(_ + 1).padStart(2, "0") }),
154
- /* @__PURE__ */ e("span", { className: "idx-card__title", children: o.title }),
155
- /* @__PURE__ */ i("span", { className: "idx-card__count", children: [
156
- u.length,
166
+ /* @__PURE__ */ e("span", { className: "idx-card__num", children: String(v + 1).padStart(2, "0") }),
167
+ /* @__PURE__ */ e("span", { className: "idx-card__title", children: c.title }),
168
+ /* @__PURE__ */ d("span", { className: "idx-card__count", children: [
169
+ t.length,
157
170
  " sections"
158
171
  ] }),
159
172
  /* @__PURE__ */ e("span", { className: "idx-card__toggle", "aria-hidden": "true", children: "⌄" })
160
173
  ]
161
174
  }
162
175
  ),
163
- n && /* @__PURE__ */ e("ol", { className: "idx-sec", children: u.map(({ section: p, slug: d }, m) => /* @__PURE__ */ e("li", { className: "idx-sec__row", children: /* @__PURE__ */ i("a", { href: `#/${d}`, children: [
164
- /* @__PURE__ */ e("span", { className: "idx-sec__num", children: m + 1 }),
165
- /* @__PURE__ */ e("span", { className: "idx-sec__title", children: p.title }),
176
+ w && /* @__PURE__ */ e("ol", { className: "idx-sec", children: t.map(({ section: k, slug: h }, g) => /* @__PURE__ */ e("li", { className: "idx-sec__row", children: /* @__PURE__ */ d("a", { href: `#/${h}`, children: [
177
+ /* @__PURE__ */ e("span", { className: "idx-sec__num", children: g + 1 }),
178
+ /* @__PURE__ */ e("span", { className: "idx-sec__title", children: k.title }),
166
179
  /* @__PURE__ */ e("span", { className: "idx-sec__chev", "aria-hidden": "true", children: "›" })
167
- ] }) }, d)) })
168
- ] }, o.id);
180
+ ] }) }, h)) })
181
+ ] }, c.id);
169
182
  }) }),
170
- /* @__PURE__ */ e("footer", { className: "idx__foot", children: /* @__PURE__ */ i("span", { className: "idx__hint", children: [
183
+ /* @__PURE__ */ e("footer", { className: "idx__foot", children: /* @__PURE__ */ d("span", { className: "idx__hint", children: [
171
184
  /* @__PURE__ */ e("kbd", { children: "→" }),
172
185
  " next · ",
173
186
  /* @__PURE__ */ e("kbd", { children: "←" }),
@@ -179,140 +192,193 @@ function U({
179
192
  ] })
180
193
  );
181
194
  }
182
- function ee(a, c) {
183
- const s = L(null), l = L(c);
184
- l.current = c;
185
- const [h, f] = b(!1), o = L(h);
186
- return o.current = h, N(() => {
187
- const n = new Audio();
188
- return n.addEventListener("ended", () => l.current()), n.addEventListener("error", () => f(!1)), s.current = n, () => n.pause();
195
+ function le(n, r) {
196
+ const s = S(null), o = S(r);
197
+ o.current = r;
198
+ const [l, u] = x(!1), m = S(l);
199
+ return m.current = l, N(() => {
200
+ const t = new Audio();
201
+ return t.addEventListener("ended", () => o.current()), t.addEventListener("error", () => u(!1)), s.current = t, () => t.pause();
189
202
  }, []), N(() => {
190
- const n = s.current;
191
- n && (n.pause(), a ? (n.src = a, n.currentTime = 0, o.current && n.play().catch(() => {
192
- })) : (n.removeAttribute("src"), n.load(), f(!1)));
193
- }, [a]), { playing: h, toggle: () => {
194
- const n = s.current;
195
- !n || !n.getAttribute("src") || (o.current ? (n.pause(), f(!1)) : (n.play().catch(() => {
196
- }), f(!0)));
203
+ const t = s.current;
204
+ t && (t.pause(), n ? (t.src = n, t.currentTime = 0, m.current && t.play().catch(() => {
205
+ })) : (t.removeAttribute("src"), t.load(), u(!1)));
206
+ }, [n]), { playing: l, toggle: () => {
207
+ const t = s.current;
208
+ !t || !t.getAttribute("src") || (m.current ? (t.pause(), u(!1)) : (t.play().catch(() => {
209
+ }), u(!0)));
197
210
  }, stop: () => {
198
- var n;
199
- (n = s.current) == null || n.pause(), f(!1);
211
+ var t;
212
+ (t = s.current) == null || t.pause(), u(!1);
200
213
  } };
201
214
  }
202
- function ce({
203
- subject: a,
204
- eyebrow: c,
215
+ const I = "graphl:theme";
216
+ function D() {
217
+ try {
218
+ const n = localStorage.getItem(I);
219
+ return n === "light" || n === "dark" ? n : null;
220
+ } catch {
221
+ return null;
222
+ }
223
+ }
224
+ function ce(n) {
225
+ try {
226
+ localStorage.setItem(I, n);
227
+ } catch {
228
+ }
229
+ }
230
+ const de = (n, r, s) => s || !r ? n : r;
231
+ function he(n, r) {
232
+ const [s, o] = x(() => r ? null : D()), l = de(n, s, r);
233
+ N(() => (document.documentElement.setAttribute("data-theme", l), () => document.documentElement.removeAttribute("data-theme")), [l]), N(() => {
234
+ if (r) return;
235
+ const m = (c) => {
236
+ (c.key === I || c.key === null) && o(D());
237
+ };
238
+ return window.addEventListener("storage", m), () => window.removeEventListener("storage", m);
239
+ }, [r]);
240
+ const u = T(() => {
241
+ const m = l === "dark" ? "light" : "dark";
242
+ ce(m);
243
+ const c = document.documentElement;
244
+ c.classList.add("theme-swap"), o(m), requestAnimationFrame(() => requestAnimationFrame(() => c.classList.remove("theme-swap")));
245
+ }, [l]);
246
+ return { theme: l, toggle: u };
247
+ }
248
+ const P = {
249
+ dark: { glyph: "☾", label: "Dark theme" },
250
+ light: { glyph: "☀", label: "Light theme" }
251
+ };
252
+ function ue({ theme: n, onToggle: r }) {
253
+ const s = n === "dark" ? "light" : "dark", o = `${P[n].label}. Switch to ${P[s].label.toLowerCase()}.`;
254
+ return /* @__PURE__ */ e("button", { className: "site__icon", type: "button", onClick: r, "aria-label": o, title: o, children: P[n].glyph });
255
+ }
256
+ function ve({
257
+ subject: n,
258
+ eyebrow: r,
205
259
  courses: s,
206
- getScene: l,
207
- audioBase: h = "/",
208
- kind: f = "courses"
260
+ getScene: o,
261
+ audioBase: l = "/",
262
+ kind: u = "courses",
263
+ theme: m = "dark"
209
264
  }) {
210
- const [o, _] = b(() => location.hash);
265
+ const [c, v] = x(() => location.hash);
211
266
  N(() => {
212
- const t = () => _(location.hash);
213
- return window.addEventListener("hashchange", t), () => window.removeEventListener("hashchange", t);
267
+ const a = () => v(location.hash);
268
+ return window.addEventListener("hashchange", a), () => window.removeEventListener("hashchange", a);
214
269
  }, []);
215
- const u = new URLSearchParams(location.search).get("capture") === "1", n = o.replace(/^#\/?/, ""), p = E(() => {
216
- if (s[n]) return s[n];
217
- for (const t of Object.values(s))
218
- if (t.sections.some((r) => j(t, r) === n)) return t;
270
+ const t = new URLSearchParams(location.search).get("capture") === "1", { theme: w, toggle: k } = he(m, t), h = c.replace(/^#\/?/, ""), g = C(() => {
271
+ if (s[h]) return s[h];
272
+ for (const a of Object.values(s))
273
+ if (a.sections.some((i) => K(a, i) === h)) return a;
219
274
  return Object.values(s)[0];
220
- }, [n, s]), d = E(() => P(p), [p]), m = E(() => Object.values(s).flatMap(P), [s]);
275
+ }, [h, s]), f = C(() => z(g), [g]), p = C(() => Object.values(s).flatMap(z), [s]);
221
276
  N(() => {
222
- if (u || !s[n]) return;
223
- const t = d[0];
224
- t && (history.replaceState(null, "", `${location.pathname}${location.search}#/${t.slug}`), _(`#/${t.slug}`));
225
- }, [u, n, d]), N(() => {
226
- u && (window.__scene = {
227
- plan: () => d.map(({ slug: t, section: r }) => ({
228
- slug: t,
229
- course: p.id,
277
+ if (t || !s[h]) return;
278
+ const a = f[0];
279
+ a && (history.replaceState(null, "", `${location.pathname}${location.search}#/${a.slug}`), v(`#/${a.slug}`));
280
+ }, [t, h, f]), N(() => {
281
+ t && (window.__scene = {
282
+ plan: () => f.map(({ slug: a, section: i }) => ({
283
+ slug: a,
284
+ course: g.id,
230
285
  // audio lives at audio/<course>/<id>.wav
231
- id: r.id,
232
- scene: r.scene,
233
- focus: r.focus ?? null
286
+ id: i.id,
287
+ scene: i.scene,
288
+ focus: i.focus ?? null
234
289
  }))
235
290
  });
236
- }, [u, d, p]);
237
- const v = z(
238
- (t) => {
239
- const r = location.hash.replace(/^#\/?/, ""), g = m.findIndex(({ slug: y }) => y === r);
240
- if (g !== -1) {
241
- const y = m[g + t];
242
- location.hash = y ? `#/${y.slug}` : "";
291
+ }, [t, f, g]);
292
+ const b = T(
293
+ (a) => {
294
+ const i = location.hash.replace(/^#\/?/, ""), _ = p.findIndex(({ slug: $ }) => $ === i);
295
+ if (_ !== -1) {
296
+ const $ = p[_ + a];
297
+ location.hash = $ ? `#/${$.slug}` : "";
243
298
  return;
244
299
  }
245
- if (r === "") {
246
- t === 1 && m[0] && (location.hash = `#/${m[0].slug}`);
300
+ if (i === "") {
301
+ a === 1 && p[0] && (location.hash = `#/${p[0].slug}`);
247
302
  return;
248
303
  }
249
- const k = t === 1 ? d[0] : d[d.length - 1];
250
- k && (location.hash = `#/${k.slug}`);
304
+ const E = a === 1 ? f[0] : f[f.length - 1];
305
+ E && (location.hash = `#/${E.slug}`);
251
306
  },
252
- [d, m]
307
+ [f, p]
253
308
  );
254
309
  N(() => {
255
- const t = (r) => {
256
- r.key !== "ArrowRight" && r.key !== "ArrowLeft" || (r.preventDefault(), v(r.key === "ArrowRight" ? 1 : -1));
310
+ const a = (i) => {
311
+ i.key !== "ArrowRight" && i.key !== "ArrowLeft" || (i.preventDefault(), b(i.key === "ArrowRight" ? 1 : -1));
257
312
  };
258
- return window.addEventListener("keydown", t), () => window.removeEventListener("keydown", t);
259
- }, [v]);
260
- const w = z(() => {
313
+ return window.addEventListener("keydown", a), () => window.removeEventListener("keydown", a);
314
+ }, [b]);
315
+ const y = T(() => {
261
316
  location.hash ? location.hash = "" : location.pathname !== "/" && (location.href = "/");
262
317
  }, []);
263
318
  N(() => {
264
- const t = (r) => {
265
- r.key === "Escape" && w();
319
+ const a = (i) => {
320
+ i.key === "Escape" && y();
266
321
  };
267
- return window.addEventListener("keydown", t), () => window.removeEventListener("keydown", t);
268
- }, [w]);
269
- const x = d.find(({ slug: t }) => t === n), H = x ? `${h}audio/${p.id}/${x.section.id}.wav` : void 0, { playing: M, toggle: A, stop: T } = ee(H, () => {
270
- const t = location.hash.replace(/^#\/?/, ""), r = m.findIndex(({ slug: k }) => k === t), g = m[r + 1];
271
- g ? location.hash = `#/${g.slug}` : T();
322
+ return window.addEventListener("keydown", a), () => window.removeEventListener("keydown", a);
323
+ }, [y]);
324
+ const L = f.find(({ slug: a }) => a === h), G = L ? `${l}audio/${g.id}/${L.section.id}.wav` : void 0, { playing: V, toggle: j, stop: R } = le(G, () => {
325
+ const a = location.hash.replace(/^#\/?/, ""), i = p.findIndex(({ slug: E }) => E === a), _ = p[i + 1];
326
+ _ ? location.hash = `#/${_.slug}` : R();
272
327
  });
273
328
  if (N(() => {
274
- const t = (r) => {
275
- if (r.key !== " " && r.code !== "Space") return;
276
- const g = r.target;
277
- g && (g.tagName === "INPUT" || g.tagName === "TEXTAREA" || g.isContentEditable) || (r.preventDefault(), A());
329
+ const a = (i) => {
330
+ if (i.key !== " " && i.code !== "Space") return;
331
+ const _ = i.target;
332
+ _ && (_.tagName === "INPUT" || _.tagName === "TEXTAREA" || _.isContentEditable) || (i.preventDefault(), j());
278
333
  };
279
- return window.addEventListener("keydown", t), () => window.removeEventListener("keydown", t);
280
- }), !n) return /* @__PURE__ */ e(U, { courses: Object.values(s), subject: a, kind: f });
281
- let $, C = null;
282
- if (x) {
283
- const t = d.findIndex(({ slug: r }) => r === n);
284
- $ = /* @__PURE__ */ e(
285
- q,
334
+ return window.addEventListener("keydown", a), () => window.removeEventListener("keydown", a);
335
+ }), !h)
336
+ return /* @__PURE__ */ e(
337
+ ie,
338
+ {
339
+ courses: Object.values(s),
340
+ subject: n,
341
+ kind: u,
342
+ actions: t ? null : /* @__PURE__ */ e(ue, { theme: w, onToggle: k })
343
+ }
344
+ );
345
+ let A, B = null;
346
+ if (L) {
347
+ const a = f.findIndex(({ slug: i }) => i === h);
348
+ A = /* @__PURE__ */ e(
349
+ ae,
286
350
  {
287
- section: x.section,
288
- getScene: l,
289
- capture: u,
290
- eyebrow: `${c ?? a.toUpperCase()} · ${p.id.toUpperCase()}`,
291
- index: t,
292
- total: d.length,
293
- onHome: w,
294
- onPrev: () => v(-1),
295
- onNext: () => v(1),
296
- narrating: M,
297
- onToggleNarration: A
351
+ section: L.section,
352
+ getScene: o,
353
+ capture: t,
354
+ eyebrow: `${r ?? n.toUpperCase()} · ${g.id.toUpperCase()}`,
355
+ index: a,
356
+ total: f.length,
357
+ onHome: y,
358
+ onPrev: () => b(-1),
359
+ onNext: () => b(1),
360
+ narrating: V,
361
+ onToggleNarration: j,
362
+ theme: w,
363
+ onToggleTheme: t ? void 0 : k
298
364
  }
299
365
  );
300
366
  } else {
301
- const t = l(n);
302
- $ = t ? /* @__PURE__ */ e("div", { className: "stage", children: /* @__PURE__ */ e(S, { scene: t }) }) : /* @__PURE__ */ i("div", { className: "stage stage--missing", children: [
367
+ const a = o(h);
368
+ A = a ? /* @__PURE__ */ e("div", { className: "stage", children: /* @__PURE__ */ e(H, { scene: a, theme: w }) }) : /* @__PURE__ */ d("div", { className: "stage stage--missing", children: [
303
369
  "no scene or slug: ",
304
- n
305
- ] }), C = u ? null : /* @__PURE__ */ e("button", { className: "brand-home", onClick: w, "aria-label": "Back to catalog (Esc)", title: "Back to catalog (Esc)", children: "GraphL" });
370
+ h
371
+ ] }), B = t ? null : /* @__PURE__ */ e("button", { className: "brand-home", onClick: y, "aria-label": "Back to catalog (Esc)", title: "Back to catalog (Esc)", children: "GraphL" });
306
372
  }
307
- return /* @__PURE__ */ i(I, { children: [
308
- C,
309
- $
373
+ return /* @__PURE__ */ d(M, { children: [
374
+ B,
375
+ A
310
376
  ] });
311
377
  }
312
378
  export {
313
- ce as ConceptApp,
314
- Y as SiteHeader,
315
- P as allSections,
316
- j as slugOf
379
+ ve as ConceptApp,
380
+ oe as SiteHeader,
381
+ z as allSections,
382
+ K as slugOf
317
383
  };
318
384
  //# sourceMappingURL=index.js.map