@kadal/deck 0.0.0-stage → 0.1.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.
Files changed (157) hide show
  1. package/LICENSE +661 -0
  2. package/LICENSING.md +40 -0
  3. package/README.md +170 -2
  4. package/dist/_virtual/_rolldown/runtime.js +13 -0
  5. package/dist/audience/index.d.ts +14 -0
  6. package/dist/audience/index.js +680 -0
  7. package/dist/backgrounds/index.d.ts +65 -0
  8. package/dist/backgrounds/index.js +604 -0
  9. package/dist/backgrounds/shared.d.ts +7 -0
  10. package/dist/backgrounds/shared.js +89 -0
  11. package/dist/cli/bin.d.ts +1 -0
  12. package/dist/cli/bin.js +8 -0
  13. package/dist/cli/host.js +111 -0
  14. package/dist/cli/index.d.ts +4 -0
  15. package/dist/cli/index.js +140 -0
  16. package/dist/cli/login.js +56 -0
  17. package/dist/cli/publish.js +159 -0
  18. package/dist/cli/vite.js +39 -0
  19. package/dist/compiler/code-meta.d.ts +33 -0
  20. package/dist/compiler/code-meta.js +81 -0
  21. package/dist/compiler/index.d.ts +8 -0
  22. package/dist/compiler/index.js +8 -0
  23. package/dist/compiler/magic-move.d.ts +26 -0
  24. package/dist/compiler/magic-move.js +33 -0
  25. package/dist/compiler/plain-text.js +7 -0
  26. package/dist/compiler/rehype-code.d.ts +18 -0
  27. package/dist/compiler/rehype-code.js +66 -0
  28. package/dist/compiler/rehype-glossary.d.ts +35 -0
  29. package/dist/compiler/rehype-glossary.js +211 -0
  30. package/dist/compiler/slots.d.ts +3 -0
  31. package/dist/compiler/slots.js +36 -0
  32. package/dist/compiler/split.d.ts +63 -0
  33. package/dist/compiler/split.js +195 -0
  34. package/dist/components/Background.d.ts +15 -0
  35. package/dist/components/Background.js +24 -0
  36. package/dist/components/Click.d.ts +54 -0
  37. package/dist/components/Click.js +129 -0
  38. package/dist/components/Code.d.ts +27 -0
  39. package/dist/components/Code.js +124 -0
  40. package/dist/components/Motion.d.ts +25 -0
  41. package/dist/components/Motion.js +45 -0
  42. package/dist/components/Sketch.d.ts +44 -0
  43. package/dist/components/Sketch.js +141 -0
  44. package/dist/components/Term.d.ts +63 -0
  45. package/dist/components/Term.js +492 -0
  46. package/dist/components/bling.d.ts +75 -0
  47. package/dist/components/bling.js +146 -0
  48. package/dist/components/content.d.ts +232 -0
  49. package/dist/components/content.js +637 -0
  50. package/dist/components/index.d.ts +10 -0
  51. package/dist/components/index.js +88 -0
  52. package/dist/core/clicks.d.ts +45 -0
  53. package/dist/core/clicks.js +75 -0
  54. package/dist/core/hash.d.ts +33 -0
  55. package/dist/core/hash.js +49 -0
  56. package/dist/core/keyboard.d.ts +17 -0
  57. package/dist/core/keyboard.js +198 -0
  58. package/dist/core/layout.d.ts +16 -0
  59. package/dist/core/layout.js +25 -0
  60. package/dist/core/model.d.ts +259 -0
  61. package/dist/core/model.js +377 -0
  62. package/dist/core/navigation.d.ts +74 -0
  63. package/dist/core/navigation.js +244 -0
  64. package/dist/core/poster.js +30 -0
  65. package/dist/core/store.d.ts +20 -0
  66. package/dist/core/store.js +64 -0
  67. package/dist/core/touch.js +27 -0
  68. package/dist/draw/DrawingLayer.js +315 -0
  69. package/dist/draw/strokes.js +188 -0
  70. package/dist/export/index.d.ts +68 -0
  71. package/dist/export/index.js +251 -0
  72. package/dist/export/run.js +81 -0
  73. package/dist/glossary/model.d.ts +81 -0
  74. package/dist/glossary/model.js +156 -0
  75. package/dist/glossary/parse.d.ts +27 -0
  76. package/dist/glossary/parse.js +90 -0
  77. package/dist/index.d.ts +31 -0
  78. package/dist/index.js +30 -0
  79. package/dist/live/LiveProvider.d.ts +20 -0
  80. package/dist/live/LiveProvider.js +450 -0
  81. package/dist/live/analytics.d.ts +15 -0
  82. package/dist/live/analytics.js +103 -0
  83. package/dist/live/client.d.ts +51 -0
  84. package/dist/live/client.js +193 -0
  85. package/dist/live/components.d.ts +64 -0
  86. package/dist/live/components.js +471 -0
  87. package/dist/live/context.d.ts +15 -0
  88. package/dist/live/context.js +28 -0
  89. package/dist/live/index.d.ts +7 -0
  90. package/dist/live/index.js +7 -0
  91. package/dist/live/protocol.d.ts +284 -0
  92. package/dist/live/protocol.js +42 -0
  93. package/dist/live/turnstile.js +49 -0
  94. package/dist/presenter/index.d.ts +20 -0
  95. package/dist/presenter/index.js +1211 -0
  96. package/dist/react/AutoAnimate.js +92 -0
  97. package/dist/react/Backgrounds.d.ts +11 -0
  98. package/dist/react/Backgrounds.js +154 -0
  99. package/dist/react/ConfettiLayer.js +85 -0
  100. package/dist/react/Deck.d.ts +35 -0
  101. package/dist/react/Deck.js +983 -0
  102. package/dist/react/Frame.d.ts +16 -0
  103. package/dist/react/Frame.js +174 -0
  104. package/dist/react/Hints.d.ts +3 -0
  105. package/dist/react/Hints.js +201 -0
  106. package/dist/react/Lightbox.js +54 -0
  107. package/dist/react/Overview.js +93 -0
  108. package/dist/react/PrintView.js +128 -0
  109. package/dist/react/ScrollView.js +128 -0
  110. package/dist/react/SlideFrame.d.ts +51 -0
  111. package/dist/react/SlideFrame.js +323 -0
  112. package/dist/react/Stage.js +151 -0
  113. package/dist/react/ThemeMenu.d.ts +5 -0
  114. package/dist/react/ThemeMenu.js +124 -0
  115. package/dist/react/Zoom.js +23 -0
  116. package/dist/react/chrome.js +339 -0
  117. package/dist/react/context.d.ts +165 -0
  118. package/dist/react/context.js +19 -0
  119. package/dist/react/hooks.d.ts +33 -0
  120. package/dist/react/hooks.js +70 -0
  121. package/dist/react/layouts.d.ts +21 -0
  122. package/dist/react/layouts.js +104 -0
  123. package/dist/react/mdx.d.ts +4 -0
  124. package/dist/react/mdx.js +33 -0
  125. package/dist/react/terms.d.ts +6 -0
  126. package/dist/react/terms.js +66 -0
  127. package/dist/renderers/Island.d.ts +57 -0
  128. package/dist/renderers/Island.js +223 -0
  129. package/dist/renderers/contract.d.ts +173 -0
  130. package/dist/renderers/contract.js +152 -0
  131. package/dist/renderers/index.d.ts +12 -0
  132. package/dist/renderers/index.js +16 -0
  133. package/dist/renderers/react.d.ts +16 -0
  134. package/dist/renderers/react.js +32 -0
  135. package/dist/renderers/web-components.d.ts +4 -0
  136. package/dist/renderers/web-components.js +103 -0
  137. package/dist/sketch/Canvas.js +49 -0
  138. package/dist/sketch/dsl.d.ts +131 -0
  139. package/dist/sketch/dsl.js +281 -0
  140. package/dist/sketch/index.d.ts +4 -0
  141. package/dist/sketch/index.js +3 -0
  142. package/dist/sketch/palette.d.ts +21 -0
  143. package/dist/sketch/palette.js +66 -0
  144. package/dist/sketch/render.d.ts +43 -0
  145. package/dist/sketch/render.js +198 -0
  146. package/dist/themes/base.css +1704 -0
  147. package/dist/themes/bling.css +592 -0
  148. package/dist/themes/glossary.css +317 -0
  149. package/dist/themes/live.css +1557 -0
  150. package/dist/themes/minimal.css +83 -0
  151. package/dist/themes/rla.css +92 -0
  152. package/dist/themes/sketch.css +121 -0
  153. package/dist/themes/stack.css +62 -0
  154. package/dist/vite/index.d.ts +15 -0
  155. package/dist/vite/index.js +320 -0
  156. package/package.json +159 -4
  157. package/tools/verify-island.mjs +150 -0
@@ -0,0 +1,492 @@
1
+ import { adhocId, buildMatcher, inlineHtml, linkLabel } from "../glossary/model.js";
2
+ import { useStore } from "../core/store.js";
3
+ import { useDeck, useSlideOptional } from "../react/context.js";
4
+ import { closeTerm, entryFor, mentionIndex, openTerm, slideKeyOf } from "../react/terms.js";
5
+ import { Icon } from "./bling.js";
6
+ import { Children, isValidElement, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
+ import { createPortal } from "react-dom";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/components/Term.tsx
10
+ /** The smallest the card's body type may get on screen, in CSS pixels. */
11
+ const MIN_BODY_PX = 13;
12
+ /** The card's body type in stage pixels at zoom 1 (the stylesheet's `--term-body`). */
13
+ const BODY_STAGE_PX = 23;
14
+ const CLOSE_GRACE = 180;
15
+ function textOf(node) {
16
+ let out = "";
17
+ Children.forEach(node, (c) => {
18
+ if (typeof c === "string" || typeof c === "number") out += String(c);
19
+ else if (isValidElement(c)) out += textOf(c.props.children);
20
+ });
21
+ return out;
22
+ }
23
+ /** The countdown cursor Paadam and the Sanket docs use: a 24 px ring whose pie fills over the delay. */
24
+ function pieCursor(fraction) {
25
+ const f = Math.max(0, Math.min(1, fraction));
26
+ const ink = "%23475569";
27
+ let fill = "";
28
+ if (f >= .999) fill = `<circle cx='12' cy='12' r='7' fill='${ink}'/>`;
29
+ else if (f > .01) {
30
+ const a = 2 * Math.PI * f;
31
+ const x = (12 + 7 * Math.sin(a)).toFixed(2);
32
+ const y = (12 - 7 * Math.cos(a)).toFixed(2);
33
+ fill = `<path d='M12 12 L12 5 A7 7 0 ${f > .5 ? 1 : 0} 1 ${x} ${y} Z' fill='${ink}'/>`;
34
+ }
35
+ return `url("data:image/svg+xml;utf8,${`<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><circle cx='12' cy='12' r='9' fill='white' fill-opacity='0.85'/><circle cx='12' cy='12' r='9' fill='none' stroke='${ink}' stroke-width='1'/>${fill}</svg>`}") 12 12, help`;
36
+ }
37
+ function Term({ id, def, more, title, full, tag, link, html, moreHtml, auto, children }) {
38
+ const deck = useDeck();
39
+ const slide = useSlideOptional();
40
+ const label = useMemo(() => textOf(children).replace(/\s+/g, " ").trim(), [children]);
41
+ const oneOff = def !== void 0 || html !== void 0;
42
+ const entry = useMemo(() => {
43
+ if (!oneOff) return entryFor(deck, id ?? label) ?? resolveByWord(deck.glossary, id ?? label);
44
+ const source = String(def ?? "");
45
+ return {
46
+ id: id ?? adhocId(`${label}|${source}`),
47
+ name: title ?? label,
48
+ full: full ?? null,
49
+ aliases: [],
50
+ tag: tag ?? null,
51
+ link: link ? {
52
+ href: link,
53
+ label: linkLabel(link)
54
+ } : null,
55
+ see: [],
56
+ auto: false,
57
+ html: {
58
+ def: html ?? inlineHtml(source.replace(/\s+/g, " ").trim()),
59
+ more: moreHtml ?? (more ? inlineHtml(more.replace(/\s+/g, " ").trim()) : null)
60
+ }
61
+ };
62
+ }, [
63
+ oneOff,
64
+ deck,
65
+ id,
66
+ label,
67
+ def,
68
+ more,
69
+ title,
70
+ full,
71
+ tag,
72
+ link,
73
+ html,
74
+ moreHtml
75
+ ]);
76
+ useEffect(() => {
77
+ if (oneOff && entry) deck.glossary.set(entry.id, entry);
78
+ }, [
79
+ oneOff,
80
+ entry,
81
+ deck.glossary
82
+ ]);
83
+ const live = !!entry && !!slide && slide.terms && deck.mode !== "print";
84
+ const ref = useRef(null);
85
+ const [shown, setShown] = useState(null);
86
+ const [arming, setArming] = useState(false);
87
+ const timers = useRef({
88
+ open: 0,
89
+ close: 0,
90
+ raf: 0
91
+ });
92
+ const delay = deck.config.glossary.delay;
93
+ const slideKey = slide ? `${slide.h},${slide.v}` : "";
94
+ useEffect(() => {
95
+ if (!live || !entry) return;
96
+ const check = (r) => {
97
+ const el = ref.current;
98
+ const mine = !!r && !!el && r.id === entry.id && r.slide === slideKey && mentionIndex(el, r.id) === r.n;
99
+ setShown(mine ? r : null);
100
+ };
101
+ check(deck.terms.get());
102
+ return deck.terms.subscribe(check);
103
+ }, [
104
+ live,
105
+ entry,
106
+ slideKey,
107
+ deck.terms
108
+ ]);
109
+ const disarm = useCallback(() => {
110
+ const t = timers.current;
111
+ if (t.open) clearTimeout(t.open);
112
+ if (t.raf) cancelAnimationFrame(t.raf);
113
+ t.open = 0;
114
+ t.raf = 0;
115
+ if (ref.current) ref.current.style.cursor = "";
116
+ setArming(false);
117
+ }, []);
118
+ const holdOpen = useCallback(() => {
119
+ if (timers.current.close) clearTimeout(timers.current.close);
120
+ timers.current.close = 0;
121
+ }, []);
122
+ useEffect(() => () => {
123
+ disarm();
124
+ holdOpen();
125
+ }, [disarm, holdOpen]);
126
+ const reveal = useCallback((pinned) => {
127
+ const el = ref.current;
128
+ if (!el || !entry) return;
129
+ const key = slideKeyOf(el);
130
+ if (!key) return;
131
+ disarm();
132
+ holdOpen();
133
+ openTerm(deck, {
134
+ slide: key,
135
+ id: entry.id,
136
+ n: mentionIndex(el, entry.id),
137
+ view: null,
138
+ pinned,
139
+ from: "local"
140
+ });
141
+ }, [
142
+ deck,
143
+ entry,
144
+ disarm,
145
+ holdOpen
146
+ ]);
147
+ const leaveSoon = useCallback(() => {
148
+ holdOpen();
149
+ timers.current.close = window.setTimeout(() => {
150
+ const now = deck.terms.get();
151
+ if (now && !now.pinned && now.from === "local") closeTerm(deck);
152
+ }, CLOSE_GRACE);
153
+ }, [deck, holdOpen]);
154
+ if (!entry) return /* @__PURE__ */ jsx(Fragment, { children });
155
+ if (!live) return /* @__PURE__ */ jsx("span", {
156
+ className: "deck-term is-inert",
157
+ "data-term": entry.id,
158
+ children
159
+ });
160
+ const cardId = `deck-term-${entry.id.replace(/[^a-z0-9-]/gi, "")}-${slideKey.replace(",", "-")}`;
161
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
162
+ ref,
163
+ role: "term",
164
+ className: [
165
+ "deck-term",
166
+ auto ? "is-auto" : "",
167
+ arming ? "is-arming" : "",
168
+ shown ? "is-open" : ""
169
+ ].filter(Boolean).join(" "),
170
+ style: { "--term-delay": `${delay}ms` },
171
+ "data-term": entry.id,
172
+ "data-hint": "",
173
+ "aria-describedby": shown ? cardId : void 0,
174
+ onPointerEnter: (e) => {
175
+ if (e.pointerType !== "mouse" || deck.ui.get().drawing) return;
176
+ holdOpen();
177
+ if (shown || timers.current.open) return;
178
+ setArming(true);
179
+ const t0 = performance.now();
180
+ let last = -1;
181
+ const tick = () => {
182
+ const f = (performance.now() - t0) / Math.max(1, delay);
183
+ if (f >= 1) return;
184
+ const step = Math.floor(f * 12);
185
+ if (step !== last && ref.current) {
186
+ last = step;
187
+ ref.current.style.cursor = pieCursor(f);
188
+ }
189
+ timers.current.raf = requestAnimationFrame(tick);
190
+ };
191
+ if (delay > 0) timers.current.raf = requestAnimationFrame(tick);
192
+ timers.current.open = window.setTimeout(() => reveal(false), delay);
193
+ },
194
+ onPointerLeave: (e) => {
195
+ if (e.pointerType !== "mouse") return;
196
+ disarm();
197
+ if (shown && !shown.pinned) leaveSoon();
198
+ },
199
+ onClick: (e) => {
200
+ e.preventDefault();
201
+ e.stopPropagation();
202
+ if (shown?.pinned && shown.from === "local") closeTerm(deck);
203
+ else reveal(true);
204
+ },
205
+ children
206
+ }), shown ? /* @__PURE__ */ jsx(TermCard, {
207
+ id: cardId,
208
+ anchor: ref,
209
+ entry,
210
+ reveal: shown,
211
+ onEnter: holdOpen,
212
+ onLeave: () => {
213
+ if (!shown.pinned && shown.from === "local") leaveSoon();
214
+ }
215
+ }) : null] });
216
+ }
217
+ let wordIndex = null;
218
+ function matcherFor(glossary) {
219
+ if (!wordIndex || wordIndex.map !== glossary) wordIndex = {
220
+ map: glossary,
221
+ matcher: buildMatcher([...glossary.values()])
222
+ };
223
+ return wordIndex.matcher;
224
+ }
225
+ /** A hand-written `<Term>` the compiler never saw (inside a custom component): find it by name. */
226
+ function resolveByWord(glossary, word) {
227
+ return matcherFor(glossary).resolve(word);
228
+ }
229
+ /**
230
+ * Links the glossary's mentions inside a plain string, at render time — for a component whose
231
+ * words arrive as a prop (`<Stat label="quantisation RMSE" />`), where the compiler, which only
232
+ * sees a slide's children, cannot reach. The first mention of each term in the string is linked.
233
+ *
234
+ * <small><Terms>{note}</Terms></small>
235
+ */
236
+ function Terms({ children }) {
237
+ const deck = useDeck();
238
+ const text = children ?? "";
239
+ const parts = useMemo(() => {
240
+ const out = [];
241
+ const seen = /* @__PURE__ */ new Set();
242
+ let last = 0;
243
+ for (const m of matcherFor(deck.glossary).find(text)) {
244
+ if (seen.has(m.entry.id)) continue;
245
+ seen.add(m.entry.id);
246
+ if (m.index > last) out.push(text.slice(last, m.index));
247
+ out.push(/* @__PURE__ */ jsx(Term, {
248
+ id: m.entry.id,
249
+ auto: true,
250
+ children: m.text
251
+ }, m.index));
252
+ last = m.index + m.text.length;
253
+ }
254
+ if (last < text.length) out.push(text.slice(last));
255
+ return out;
256
+ }, [deck.glossary, text]);
257
+ return /* @__PURE__ */ jsx(Fragment, { children: parts });
258
+ }
259
+ function TermCard({ id, anchor, entry, reveal, onEnter, onLeave }) {
260
+ const deck = useDeck();
261
+ const card = useRef(null);
262
+ const [host, setHost] = useState(null);
263
+ const [place, setPlace] = useState({
264
+ left: 0,
265
+ top: 0,
266
+ arrow: 40,
267
+ above: true,
268
+ zoom: 1,
269
+ ready: false
270
+ });
271
+ const sync = useStore(deck.ui, (s) => s.termSync);
272
+ const viewed = (reveal.view ? entryFor(deck, reveal.view) : void 0) ?? entry;
273
+ useLayoutEffect(() => {
274
+ const slide = anchor.current?.closest(".deck-slide") ?? null;
275
+ setHost(slide && !slide.closest("[data-term-cards='off']") ? slide : null);
276
+ }, [anchor]);
277
+ useLayoutEffect(() => {
278
+ const el = anchor.current;
279
+ const box = card.current;
280
+ if (!el || !box || !host) return;
281
+ const measure = () => {
282
+ const sr = host.getBoundingClientRect();
283
+ const scale = sr.width / host.offsetWidth || 1;
284
+ const zoom = Math.min(3.2, Math.max(1, MIN_BODY_PX / (BODY_STAGE_PX * scale)));
285
+ const tr = el.getClientRects()[0] ?? el.getBoundingClientRect();
286
+ const W = host.offsetWidth;
287
+ const H = host.offsetHeight;
288
+ const x = (tr.left - sr.left) / scale;
289
+ const y = (tr.top - sr.top) / scale;
290
+ const w = tr.width / scale;
291
+ const h = tr.height / scale;
292
+ const cw = box.offsetWidth;
293
+ const ch = box.offsetHeight;
294
+ const gap = 16 * zoom;
295
+ const pad = 20;
296
+ const roomAbove = y - gap - pad;
297
+ const roomBelow = H - (y + h) - gap - pad;
298
+ const above = roomAbove >= ch || roomAbove >= roomBelow;
299
+ const left = Math.min(Math.max(pad, x + w / 2 - cw / 2), Math.max(pad, W - cw - pad));
300
+ const top = above ? Math.max(pad, y - gap - ch) : Math.min(y + h + gap, H - ch - pad);
301
+ const arrow = Math.min(Math.max(28 * zoom, x + w / 2 - left), cw - 28 * zoom);
302
+ setPlace((p) => p.ready && Math.abs(p.left - left) < .5 && Math.abs(p.top - top) < .5 && Math.abs(p.arrow - arrow) < .5 && p.above === above && p.zoom === zoom ? p : {
303
+ left,
304
+ top,
305
+ arrow,
306
+ above,
307
+ zoom,
308
+ ready: true
309
+ });
310
+ };
311
+ measure();
312
+ const ro = new ResizeObserver(measure);
313
+ ro.observe(host);
314
+ ro.observe(box);
315
+ window.addEventListener("resize", measure);
316
+ return () => {
317
+ ro.disconnect();
318
+ window.removeEventListener("resize", measure);
319
+ };
320
+ }, [
321
+ anchor,
322
+ host,
323
+ viewed
324
+ ]);
325
+ if (!host) return null;
326
+ const swap = (view) => {
327
+ const now = deck.terms.get();
328
+ if (now) openTerm(deck, {
329
+ ...now,
330
+ view,
331
+ pinned: true,
332
+ from: "local"
333
+ });
334
+ };
335
+ return createPortal(/* @__PURE__ */ jsxs("div", {
336
+ ref: card,
337
+ id,
338
+ role: "tooltip",
339
+ className: [
340
+ "deck-term-card",
341
+ place.above ? "is-above" : "is-below",
342
+ place.ready ? "is-ready" : "",
343
+ reveal.pinned ? "is-pinned" : ""
344
+ ].filter(Boolean).join(" "),
345
+ style: {
346
+ left: place.left,
347
+ top: place.top,
348
+ "--term-arrow": `${place.arrow}px`,
349
+ "--term-zoom": place.zoom
350
+ },
351
+ onPointerEnter: onEnter,
352
+ onPointerLeave: onLeave,
353
+ onClick: (e) => e.stopPropagation(),
354
+ children: [/* @__PURE__ */ jsx(TermBody, {
355
+ entry: viewed,
356
+ origin: viewed === entry ? null : entry,
357
+ onView: swap,
358
+ badge: reveal.from === "remote" ? {
359
+ icon: "radio",
360
+ text: "From the presenter"
361
+ } : deck.isPresenter && sync ? {
362
+ icon: "radio",
363
+ text: "On every screen"
364
+ } : null
365
+ }), reveal.pinned ? /* @__PURE__ */ jsx("button", {
366
+ type: "button",
367
+ className: "deck-term-close",
368
+ "aria-label": "Close",
369
+ onClick: () => closeTerm(deck),
370
+ children: /* @__PURE__ */ jsx(Icon, { name: "x" })
371
+ }) : null]
372
+ }), host);
373
+ }
374
+ /** What a definition says, shared by the card on the slide and the readout on a phone. */
375
+ function TermBody({ entry, origin, onView, badge }) {
376
+ const deck = useDeck();
377
+ const related = entry.see.map((id) => entryFor(deck, id)).filter((e) => !!e && e.id !== origin?.id);
378
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
379
+ /* @__PURE__ */ jsxs("div", {
380
+ className: "deck-term-head",
381
+ children: [
382
+ /* @__PURE__ */ jsx(Icon, {
383
+ name: "book-open-text",
384
+ className: "deck-term-icon"
385
+ }),
386
+ /* @__PURE__ */ jsx("span", {
387
+ className: "deck-term-name",
388
+ children: entry.name
389
+ }),
390
+ entry.tag ? /* @__PURE__ */ jsx("span", {
391
+ className: "deck-term-tag",
392
+ children: entry.tag
393
+ }) : null
394
+ ]
395
+ }),
396
+ entry.full ? /* @__PURE__ */ jsx("div", {
397
+ className: "deck-term-full",
398
+ children: entry.full
399
+ }) : null,
400
+ /* @__PURE__ */ jsx("p", {
401
+ className: "deck-term-def",
402
+ dangerouslySetInnerHTML: { __html: entry.html.def }
403
+ }),
404
+ entry.html.more ? /* @__PURE__ */ jsx("p", {
405
+ className: "deck-term-more",
406
+ dangerouslySetInnerHTML: { __html: entry.html.more }
407
+ }) : null,
408
+ origin || related.length || entry.link || badge ? /* @__PURE__ */ jsxs("div", {
409
+ className: "deck-term-foot",
410
+ children: [
411
+ origin ? /* @__PURE__ */ jsxs("button", {
412
+ type: "button",
413
+ className: "deck-term-see is-back",
414
+ onClick: () => onView(null),
415
+ children: [
416
+ /* @__PURE__ */ jsx(Icon, { name: "arrow-left" }),
417
+ " ",
418
+ origin.name
419
+ ]
420
+ }) : null,
421
+ related.length ? /* @__PURE__ */ jsx("span", {
422
+ className: "deck-term-see-label",
423
+ children: "See also"
424
+ }) : null,
425
+ related.map((r) => /* @__PURE__ */ jsx("button", {
426
+ type: "button",
427
+ className: "deck-term-see",
428
+ onClick: () => onView(r.id),
429
+ children: r.name
430
+ }, r.id)),
431
+ entry.link ? /* @__PURE__ */ jsxs("a", {
432
+ className: "deck-term-link",
433
+ href: entry.link.href,
434
+ target: "_blank",
435
+ rel: "noopener noreferrer",
436
+ "data-no-preview": "",
437
+ children: [
438
+ entry.link.label,
439
+ " ",
440
+ /* @__PURE__ */ jsx(Icon, { name: "arrow-up-right" })
441
+ ]
442
+ }) : null,
443
+ badge ? /* @__PURE__ */ jsxs("span", {
444
+ className: "deck-term-badge",
445
+ children: [
446
+ /* @__PURE__ */ jsx(Icon, { name: badge.icon }),
447
+ " ",
448
+ badge.text
449
+ ]
450
+ }) : null
451
+ ]
452
+ }) : null
453
+ ] });
454
+ }
455
+ /**
456
+ * The window's open definition as ordinary page content, at reading size — for a page whose slide
457
+ * is too small to carry a card (the audience's phone). Put the mirror inside an element with
458
+ * `data-term-cards="off"` and this beside it.
459
+ */
460
+ function TermReadout({ className }) {
461
+ const deck = useDeck();
462
+ const reveal = useStore(deck.terms);
463
+ const entry = reveal ? entryFor(deck, reveal.id) : void 0;
464
+ if (!reveal || !entry) return null;
465
+ const viewed = (reveal.view ? entryFor(deck, reveal.view) : void 0) ?? entry;
466
+ return /* @__PURE__ */ jsxs("div", {
467
+ className: ["deck-term-readout", className].filter(Boolean).join(" "),
468
+ role: "status",
469
+ children: [/* @__PURE__ */ jsx(TermBody, {
470
+ entry: viewed,
471
+ origin: viewed === entry ? null : entry,
472
+ onView: (view) => openTerm(deck, {
473
+ ...reveal,
474
+ view,
475
+ pinned: true,
476
+ from: "local"
477
+ }),
478
+ badge: reveal.from === "remote" ? {
479
+ icon: "radio",
480
+ text: "From the presenter"
481
+ } : null
482
+ }), /* @__PURE__ */ jsx("button", {
483
+ type: "button",
484
+ className: "deck-term-close",
485
+ "aria-label": "Close",
486
+ onClick: () => closeTerm(deck),
487
+ children: /* @__PURE__ */ jsx(Icon, { name: "x" })
488
+ })]
489
+ });
490
+ }
491
+ //#endregion
492
+ export { Term, TermReadout, Terms };
@@ -0,0 +1,75 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/components/bling.d.ts
3
+ export type Tone = "accent" | "accent-2" | "good" | "warn" | "bad" | "info" | "neutral" | "purple" | "pink" | "amber" | "teal";
4
+ export declare function GradientText({ children, from, to, angle, className, as: Tag }: {
5
+ children?: ReactNode;
6
+ from?: string;
7
+ to?: string;
8
+ angle?: number;
9
+ className?: string;
10
+ as?: "span" | "h1" | "h2" | "h3" | "strong";
11
+ }): import("react").JSX.Element;
12
+ export declare function Glass({ children, className, tone, pad, glow, style }: {
13
+ children?: ReactNode;
14
+ className?: string;
15
+ tone?: Tone;
16
+ pad?: string;
17
+ glow?: boolean;
18
+ style?: React.CSSProperties;
19
+ }): import("react").JSX.Element;
20
+ export declare function Glow({ color, size, x, y, opacity, className }: {
21
+ color?: string;
22
+ size?: string;
23
+ x?: string;
24
+ y?: string;
25
+ opacity?: number;
26
+ className?: string;
27
+ }): import("react").JSX.Element;
28
+ export declare function Chip({ children, tone, icon, className }: {
29
+ children?: ReactNode;
30
+ tone?: Tone;
31
+ icon?: string;
32
+ className?: string;
33
+ }): import("react").JSX.Element;
34
+ export declare function Icon({ name, size, color, strokeWidth, className }: {
35
+ name: string;
36
+ size?: string | number;
37
+ color?: string;
38
+ strokeWidth?: number;
39
+ className?: string;
40
+ }): import("react").JSX.Element;
41
+ export declare function Divider({ label, className }: {
42
+ label?: ReactNode;
43
+ className?: string;
44
+ }): import("react").JSX.Element;
45
+ export declare function Ribbon({ children, tone, corner }: {
46
+ children?: ReactNode;
47
+ tone?: Tone;
48
+ corner?: "left" | "right";
49
+ }): import("react").JSX.Element;
50
+ export declare function Halo({ children, tone, size, className }: {
51
+ children?: ReactNode;
52
+ tone?: Tone;
53
+ size?: string;
54
+ className?: string;
55
+ }): import("react").JSX.Element;
56
+ export declare function Shimmer({ children, className, as: Tag }: {
57
+ children?: ReactNode;
58
+ className?: string;
59
+ as?: "span" | "h1" | "h2" | "strong";
60
+ }): import("react").JSX.Element;
61
+ export declare function Blob({ color, x, y, size, rotate, opacity, className }: {
62
+ color?: string;
63
+ x?: string;
64
+ y?: string;
65
+ size?: string;
66
+ rotate?: number;
67
+ opacity?: number;
68
+ className?: string;
69
+ }): import("react").JSX.Element;
70
+ export declare function Tilt({ children, className, max }: {
71
+ children?: ReactNode;
72
+ className?: string;
73
+ max?: number;
74
+ }): import("react").JSX.Element;
75
+ //#endregion