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