@anvpx/motion-scenes 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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -2
  3. package/dist/catalog.d.ts +17 -0
  4. package/dist/catalog.js +51 -0
  5. package/dist/custom.d.ts +86 -0
  6. package/dist/custom.js +170 -0
  7. package/dist/define-scene.d.ts +31 -0
  8. package/dist/define-scene.js +128 -0
  9. package/dist/html.d.ts +5 -0
  10. package/dist/html.js +14 -0
  11. package/dist/index.d.ts +11 -0
  12. package/dist/index.js +11 -0
  13. package/dist/position.d.ts +21 -0
  14. package/dist/position.js +16 -0
  15. package/dist/registry.d.ts +4 -0
  16. package/dist/registry.js +68 -0
  17. package/dist/runtime/easing.d.ts +14 -0
  18. package/dist/runtime/easing.js +45 -0
  19. package/dist/runtime/mount.d.ts +50 -0
  20. package/dist/runtime/mount.js +153 -0
  21. package/dist/runtime/timeline.d.ts +147 -0
  22. package/dist/runtime/timeline.js +199 -0
  23. package/dist/runtime-files.d.ts +2 -0
  24. package/dist/runtime-files.js +15 -0
  25. package/dist/scene-contract.d.ts +7 -0
  26. package/dist/scene-contract.js +104 -0
  27. package/dist/scene3d/post.d.ts +12 -0
  28. package/dist/scene3d/post.js +16 -0
  29. package/dist/scene3d/reflector.d.ts +9 -0
  30. package/dist/scene3d/reflector.js +17 -0
  31. package/dist/scene3d/textures.d.ts +9 -0
  32. package/dist/scene3d/textures.js +11 -0
  33. package/dist/scene3d/volumetric.d.ts +9 -0
  34. package/dist/scene3d/volumetric.js +16 -0
  35. package/dist/scenes/bug/index.d.ts +1 -0
  36. package/dist/scenes/bug/index.js +45 -0
  37. package/dist/scenes/bug/scene.d.ts +2 -0
  38. package/dist/scenes/bug/scene.js +6 -0
  39. package/dist/scenes/bug/style.css +30 -0
  40. package/dist/scenes/bullets/index.d.ts +1 -0
  41. package/dist/scenes/bullets/index.js +128 -0
  42. package/dist/scenes/bullets/scene.d.ts +2 -0
  43. package/dist/scenes/bullets/scene.js +6 -0
  44. package/dist/scenes/bullets/style.css +91 -0
  45. package/dist/scenes/callout/index.d.ts +9 -0
  46. package/dist/scenes/callout/index.js +95 -0
  47. package/dist/scenes/callout/scene.d.ts +2 -0
  48. package/dist/scenes/callout/scene.js +43 -0
  49. package/dist/scenes/callout/style.css +97 -0
  50. package/dist/scenes/caption/index.d.ts +1 -0
  51. package/dist/scenes/caption/index.js +72 -0
  52. package/dist/scenes/caption/scene.d.ts +11 -0
  53. package/dist/scenes/caption/scene.js +33 -0
  54. package/dist/scenes/caption/style.css +33 -0
  55. package/dist/scenes/chapter/index.d.ts +1 -0
  56. package/dist/scenes/chapter/index.js +29 -0
  57. package/dist/scenes/chapter/scene.d.ts +2 -0
  58. package/dist/scenes/chapter/scene.js +6 -0
  59. package/dist/scenes/chapter/style.css +25 -0
  60. package/dist/scenes/chart/index.d.ts +1 -0
  61. package/dist/scenes/chart/index.js +24 -0
  62. package/dist/scenes/chart/render.d.ts +4 -0
  63. package/dist/scenes/chart/render.js +257 -0
  64. package/dist/scenes/chart/scale.d.ts +11 -0
  65. package/dist/scenes/chart/scale.js +53 -0
  66. package/dist/scenes/chart/scene.d.ts +2 -0
  67. package/dist/scenes/chart/scene.js +73 -0
  68. package/dist/scenes/chart/schema.d.ts +36 -0
  69. package/dist/scenes/chart/schema.js +109 -0
  70. package/dist/scenes/chart/style.css +425 -0
  71. package/dist/scenes/code/index.d.ts +1 -0
  72. package/dist/scenes/code/index.js +109 -0
  73. package/dist/scenes/code/scene.d.ts +2 -0
  74. package/dist/scenes/code/scene.js +108 -0
  75. package/dist/scenes/code/style.css +155 -0
  76. package/dist/scenes/code/tokenize.d.ts +8 -0
  77. package/dist/scenes/code/tokenize.js +145 -0
  78. package/dist/scenes/compare/index.d.ts +1 -0
  79. package/dist/scenes/compare/index.js +128 -0
  80. package/dist/scenes/compare/scene.d.ts +2 -0
  81. package/dist/scenes/compare/scene.js +58 -0
  82. package/dist/scenes/compare/style.css +187 -0
  83. package/dist/scenes/countdown/index.d.ts +1 -0
  84. package/dist/scenes/countdown/index.js +46 -0
  85. package/dist/scenes/countdown/scene.d.ts +2 -0
  86. package/dist/scenes/countdown/scene.js +21 -0
  87. package/dist/scenes/countdown/style.css +32 -0
  88. package/dist/scenes/flow/index.d.ts +1 -0
  89. package/dist/scenes/flow/index.js +66 -0
  90. package/dist/scenes/flow/inspect.d.ts +26 -0
  91. package/dist/scenes/flow/inspect.js +25 -0
  92. package/dist/scenes/flow/layout.d.ts +83 -0
  93. package/dist/scenes/flow/layout.js +222 -0
  94. package/dist/scenes/flow/measure.d.ts +15 -0
  95. package/dist/scenes/flow/measure.js +33 -0
  96. package/dist/scenes/flow/plan.d.ts +42 -0
  97. package/dist/scenes/flow/plan.js +117 -0
  98. package/dist/scenes/flow/scene.d.ts +2 -0
  99. package/dist/scenes/flow/scene.js +6 -0
  100. package/dist/scenes/flow/schema.d.ts +49 -0
  101. package/dist/scenes/flow/schema.js +60 -0
  102. package/dist/scenes/flow/style.css +54 -0
  103. package/dist/scenes/hero/index.d.ts +3 -0
  104. package/dist/scenes/hero/index.js +77 -0
  105. package/dist/scenes/hero/scene.d.ts +2 -0
  106. package/dist/scenes/hero/scene.js +6 -0
  107. package/dist/scenes/hero/style.css +143 -0
  108. package/dist/scenes/icon-grid/index.d.ts +1 -0
  109. package/dist/scenes/icon-grid/index.js +69 -0
  110. package/dist/scenes/icon-grid/scene.d.ts +2 -0
  111. package/dist/scenes/icon-grid/scene.js +6 -0
  112. package/dist/scenes/icon-grid/style.css +74 -0
  113. package/dist/scenes/kinetic/index.d.ts +1 -0
  114. package/dist/scenes/kinetic/index.js +62 -0
  115. package/dist/scenes/kinetic/scene.d.ts +2 -0
  116. package/dist/scenes/kinetic/scene.js +98 -0
  117. package/dist/scenes/kinetic/style.css +94 -0
  118. package/dist/scenes/kpi-grid/index.d.ts +1 -0
  119. package/dist/scenes/kpi-grid/index.js +161 -0
  120. package/dist/scenes/kpi-grid/scene.d.ts +2 -0
  121. package/dist/scenes/kpi-grid/scene.js +6 -0
  122. package/dist/scenes/kpi-grid/style.css +141 -0
  123. package/dist/scenes/lower-third/index.d.ts +1 -0
  124. package/dist/scenes/lower-third/index.js +33 -0
  125. package/dist/scenes/lower-third/scene.d.ts +2 -0
  126. package/dist/scenes/lower-third/scene.js +6 -0
  127. package/dist/scenes/lower-third/style.css +45 -0
  128. package/dist/scenes/media-frame/index.d.ts +1 -0
  129. package/dist/scenes/media-frame/index.js +99 -0
  130. package/dist/scenes/media-frame/scene.d.ts +2 -0
  131. package/dist/scenes/media-frame/scene.js +85 -0
  132. package/dist/scenes/media-frame/style.css +275 -0
  133. package/dist/scenes/outro/index.d.ts +1 -0
  134. package/dist/scenes/outro/index.js +54 -0
  135. package/dist/scenes/outro/scene.d.ts +2 -0
  136. package/dist/scenes/outro/scene.js +6 -0
  137. package/dist/scenes/outro/style.css +92 -0
  138. package/dist/scenes/poster/index.d.ts +3 -0
  139. package/dist/scenes/poster/index.js +62 -0
  140. package/dist/scenes/poster/scene.d.ts +2 -0
  141. package/dist/scenes/poster/scene.js +6 -0
  142. package/dist/scenes/poster/style.css +102 -0
  143. package/dist/scenes/progress/index.d.ts +1 -0
  144. package/dist/scenes/progress/index.js +40 -0
  145. package/dist/scenes/progress/scene.d.ts +2 -0
  146. package/dist/scenes/progress/scene.js +17 -0
  147. package/dist/scenes/progress/style.css +31 -0
  148. package/dist/scenes/quote/index.d.ts +3 -0
  149. package/dist/scenes/quote/index.js +69 -0
  150. package/dist/scenes/quote/scene.d.ts +2 -0
  151. package/dist/scenes/quote/scene.js +6 -0
  152. package/dist/scenes/quote/style.css +86 -0
  153. package/dist/scenes/scene3d/index.d.ts +1 -0
  154. package/dist/scenes/scene3d/index.js +58 -0
  155. package/dist/scenes/scene3d/scene.d.ts +2 -0
  156. package/dist/scenes/scene3d/scene.js +413 -0
  157. package/dist/scenes/scene3d/style.css +43 -0
  158. package/dist/scenes/stat/index.d.ts +4 -0
  159. package/dist/scenes/stat/index.js +72 -0
  160. package/dist/scenes/stat/scene.d.ts +2 -0
  161. package/dist/scenes/stat/scene.js +6 -0
  162. package/dist/scenes/stat/style.css +95 -0
  163. package/dist/scenes/steps/index.d.ts +1 -0
  164. package/dist/scenes/steps/index.js +147 -0
  165. package/dist/scenes/steps/scene.d.ts +2 -0
  166. package/dist/scenes/steps/scene.js +6 -0
  167. package/dist/scenes/steps/style.css +190 -0
  168. package/dist/scenes/sticker/index.d.ts +1 -0
  169. package/dist/scenes/sticker/index.js +48 -0
  170. package/dist/scenes/sticker/scene.d.ts +2 -0
  171. package/dist/scenes/sticker/scene.js +57 -0
  172. package/dist/scenes/sticker/style.css +88 -0
  173. package/dist/scenes/table/index.d.ts +1 -0
  174. package/dist/scenes/table/index.js +131 -0
  175. package/dist/scenes/table/scene.d.ts +2 -0
  176. package/dist/scenes/table/scene.js +6 -0
  177. package/dist/scenes/table/style.css +102 -0
  178. package/dist/scenes/timeline/index.d.ts +1 -0
  179. package/dist/scenes/timeline/index.js +68 -0
  180. package/dist/scenes/timeline/scene.d.ts +2 -0
  181. package/dist/scenes/timeline/scene.js +6 -0
  182. package/dist/scenes/timeline/style.css +161 -0
  183. package/dist/scenes/title/index.d.ts +1 -0
  184. package/dist/scenes/title/index.js +52 -0
  185. package/dist/scenes/title/scene.d.ts +2 -0
  186. package/dist/scenes/title/scene.js +6 -0
  187. package/dist/scenes/title/style.css +80 -0
  188. package/dist/scenes/waveform/index.d.ts +1 -0
  189. package/dist/scenes/waveform/index.js +39 -0
  190. package/dist/scenes/waveform/scene.d.ts +2 -0
  191. package/dist/scenes/waveform/scene.js +35 -0
  192. package/dist/scenes/waveform/style.css +44 -0
  193. package/dist/shared/fit-text.d.ts +24 -0
  194. package/dist/shared/fit-text.js +82 -0
  195. package/dist/shared/format.d.ts +24 -0
  196. package/dist/shared/format.js +59 -0
  197. package/dist/shared/frame.d.ts +14 -0
  198. package/dist/shared/frame.js +21 -0
  199. package/dist/shared/icon-search.d.ts +9 -0
  200. package/dist/shared/icon-search.js +26 -0
  201. package/dist/shared/icon.d.ts +15 -0
  202. package/dist/shared/icon.js +28 -0
  203. package/dist/shared/index.d.ts +11 -0
  204. package/dist/shared/index.js +7 -0
  205. package/dist/shared/markup.d.ts +12 -0
  206. package/dist/shared/markup.js +43 -0
  207. package/dist/shared/measure.d.ts +7 -0
  208. package/dist/shared/measure.js +34 -0
  209. package/dist/shared/motion.d.ts +94 -0
  210. package/dist/shared/motion.js +117 -0
  211. package/dist/source-hash.d.ts +4 -0
  212. package/dist/source-hash.js +26 -0
  213. package/dist/test-fixtures.d.ts +9 -0
  214. package/dist/test-fixtures.js +22 -0
  215. package/dist/text-file.d.ts +1 -0
  216. package/dist/text-file.js +3 -0
  217. package/dist/words.d.ts +11 -0
  218. package/dist/words.js +93 -0
  219. package/package.json +36 -3
@@ -0,0 +1,95 @@
1
+ import { z } from "zod";
2
+ import { defineScene } from "../../define-scene.js";
3
+ import { animAttrs, esc } from "../../html.js";
4
+ import { sceneIcon } from "../../shared/icon.js";
5
+ import { accentSegments, stripAccent } from "../../shared/markup.js";
6
+ import { readText } from "../../text-file.js";
7
+ import { limitWords, present, text } from "../../words.js";
8
+ const MAX_CHARS = 120;
9
+ const ICON_SIZE = 48;
10
+ const KINDS = ["formula", "statement", "definition", "warning", "tip"];
11
+ const KIND_ICON = {
12
+ formula: "sigma",
13
+ statement: "quote",
14
+ definition: "book-open",
15
+ warning: "triangle-alert",
16
+ tip: "lightbulb",
17
+ };
18
+ /** Text of at most this many characters (accent brackets excluded) gets the next size up. */
19
+ const SIZE_STEPS = {
20
+ formula: [16, 40],
21
+ statement: [28, 70],
22
+ definition: [28, 70],
23
+ warning: [28, 70],
24
+ tip: [28, 70],
25
+ };
26
+ const capped = (max) => text.max(max, `max ${max} characters`);
27
+ const configSchema = limitWords(z.strictObject({
28
+ text: capped(MAX_CHARS),
29
+ kind: z.enum(KINDS).default("statement"),
30
+ label: capped(30).optional(),
31
+ note: capped(80).optional(),
32
+ }), (props) => [...present(stripAccent(props.text), props.label, props.note)]);
33
+ const OPERATOR = /^[+\-−–×÷*/=<>≈≠≤≥·^%→⇒±∑∏√]+$/;
34
+ /** Words of accent markup as tokens: split at spaces, adjacent pieces (`[c]²`) stay together. */
35
+ export const tokenize = (source) => {
36
+ const tokens = [];
37
+ let pieces = [];
38
+ const flush = () => {
39
+ if (pieces.length === 0)
40
+ return;
41
+ const plain = pieces.every((p) => !p.accent);
42
+ const joined = pieces.map((p) => p.text).join("");
43
+ tokens.push({ pieces, op: plain && OPERATOR.test(joined) });
44
+ pieces = [];
45
+ };
46
+ for (const segment of accentSegments(source)) {
47
+ segment.text.split(/(\s+)/).forEach((part, index) => {
48
+ if (index % 2 === 1)
49
+ flush();
50
+ else if (part !== "")
51
+ pieces.push({ text: part, accent: segment.accent });
52
+ });
53
+ }
54
+ flush();
55
+ return tokens;
56
+ };
57
+ const termHtml = (token, index) => {
58
+ const inner = token.pieces
59
+ .map((p) => (p.accent ? `<span class="accent">${esc(p.text)}</span>` : esc(p.text)))
60
+ .join("");
61
+ return `<span class="mk-term${token.op ? " mk-op" : ""}" data-term="${index}">${inner}</span>`;
62
+ };
63
+ /** Size class by visible length so the text fills the card without crowding it. */
64
+ const sizeClass = (kind, visible) => {
65
+ const [big, mid] = SIZE_STEPS[kind];
66
+ if (visible <= big)
67
+ return "mk-size-1";
68
+ return visible <= mid ? "mk-size-2" : "mk-size-3";
69
+ };
70
+ const render = (props) => {
71
+ const plain = stripAccent(props.text);
72
+ const terms = tokenize(props.text).map(termHtml).join(" ");
73
+ const icon = sceneIcon(KIND_ICON[props.kind], { at: "props.kind", size: ICON_SIZE });
74
+ const label = props.label ? `<span class="mk-label">${esc(props.label)}</span>` : "";
75
+ const note = props.note
76
+ ? `<p class="mk-note" ${animAttrs("rise", 6, "note")}>${esc(props.note)}</p>`
77
+ : "";
78
+ return `<div class="mk-card mk-${props.kind} ${sizeClass(props.kind, plain.length)}">
79
+ <div class="mk-kind" ${animAttrs("rise", 0, "label")}><span class="mk-icon">${icon}</span>${label}</div>
80
+ <p class="mk-text" aria-label="${esc(plain)}">${terms}</p>
81
+ <span class="mk-rule" data-rule></span>
82
+ ${note}
83
+ </div>`;
84
+ };
85
+ export const calloutScene = defineScene({
86
+ type: "callout",
87
+ configSchema,
88
+ meta: {
89
+ purpose: "One formula, rule, definition, warning or tip made big; [x] marks accent terms, revealed term by term.",
90
+ example: { text: "[Profit] = [Revenue] − [Costs]", kind: "formula" },
91
+ },
92
+ style: readText("./style.css", import.meta.url),
93
+ script: readText("./scene.js", import.meta.url),
94
+ render: (props) => ({ body: render(props) }),
95
+ });
@@ -0,0 +1,2 @@
1
+ /** @type {import("../../runtime/mount.js").CreateScene} */
2
+ export declare function createScene(root: HTMLElement, config: import("../../runtime/mount.js").SceneConfig, ctx: import("../../runtime/mount.js").RuntimeContext): import("../../runtime/mount.js").Runtime;
@@ -0,0 +1,43 @@
1
+ // @ts-check
2
+ import { ease } from "../../runtime/easing.js";
3
+ import { mountTimeline } from "../../runtime/mount.js";
4
+ import { progress, revealProgress } from "../../shared/motion.js";
5
+ const DEFAULT_DURATION = 6;
6
+ const START = 0.25;
7
+ const MAX_SPAN = 2.4;
8
+ const SPAN_SHARE = 0.4;
9
+ /** Opacity of a term before its reveal, so the frame is never blank. */
10
+ const FLOOR = 0.12;
11
+ const RISE_PX = 24;
12
+ /** @type {import("../../runtime/mount.js").CreateScene} */
13
+ export function createScene(root, config, ctx) {
14
+ const inner = mountTimeline(root, config, ctx, { baseDelay: 0.1, staggerFrames: 5, rise: 28 });
15
+ const duration = ctx.duration || Number(root.dataset.duration) || DEFAULT_DURATION;
16
+ const span = Math.min(MAX_SPAN, duration * SPAN_SHARE);
17
+ const terms = /** @type {HTMLElement[]} */ (Array.from(root.querySelectorAll("[data-term]")));
18
+ const rule = /** @type {HTMLElement | null} */ (root.querySelector("[data-rule]"));
19
+ const dimmed = terms.map((el) => el.classList.contains("mk-op"));
20
+ /** @param {number} t */
21
+ const seek = (t) => {
22
+ inner.seek(t);
23
+ const shown = revealProgress(terms.length, Math.max(0, t - START), span);
24
+ terms.forEach((el, i) => {
25
+ const p = shown[i] ?? 0;
26
+ const target = dimmed[i] ? 0.45 : 1;
27
+ el.style.opacity = String(FLOOR + (target - FLOOR) * p);
28
+ el.style.transform = `translateY(${(1 - p) * RISE_PX}px)`;
29
+ });
30
+ if (rule)
31
+ rule.style.transform = `scaleX(${ease("out", progress(t, START, START + span))})`;
32
+ };
33
+ const dispose = () => {
34
+ inner.dispose();
35
+ for (const el of terms) {
36
+ el.style.removeProperty("opacity");
37
+ el.style.removeProperty("transform");
38
+ }
39
+ rule?.style.removeProperty("transform");
40
+ };
41
+ seek(0);
42
+ return { seek, dispose, ready: inner.ready };
43
+ }
@@ -0,0 +1,97 @@
1
+ #scene-root .mk-card {
2
+ --k: var(--mk-color-accent);
3
+ position: relative;
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: flex-start;
7
+ gap: var(--mk-space-lg);
8
+ padding: var(--mk-space-xl) var(--mk-space-xxl);
9
+ background: var(--mk-color-card);
10
+ border: var(--mk-stroke-card, 2px) solid var(--mk-color-divider);
11
+ border-radius: var(--mk-radius-xl);
12
+ box-shadow:
13
+ inset 16px 0 0 var(--k),
14
+ 0 24px 56px -28px color-mix(in srgb, var(--mk-color-fg) 40%, transparent);
15
+ }
16
+ #scene-root .mk-warning {
17
+ --k: var(--mk-chart-2);
18
+ }
19
+ #scene-root .mk-tip {
20
+ --k: var(--mk-color-accent2);
21
+ }
22
+ #scene-root .mk-formula {
23
+ align-items: center;
24
+ text-align: center;
25
+ box-shadow:
26
+ inset 0 16px 0 var(--k),
27
+ 0 24px 56px -28px color-mix(in srgb, var(--mk-color-fg) 40%, transparent);
28
+ background: color-mix(in srgb, var(--mk-color-accentSoft) 45%, var(--mk-color-card));
29
+ }
30
+ #scene-root .mk-kind {
31
+ display: flex;
32
+ align-items: center;
33
+ gap: var(--mk-space-md);
34
+ color: var(--k);
35
+ }
36
+ #scene-root .mk-icon {
37
+ display: grid;
38
+ place-items: center;
39
+ width: 80px;
40
+ height: 80px;
41
+ border-radius: var(--mk-radius-full);
42
+ background: color-mix(in srgb, var(--k) 16%, var(--mk-color-card));
43
+ }
44
+ #scene-root .mk-label {
45
+ font-size: var(--mk-font-size-md);
46
+ font-weight: 800;
47
+ letter-spacing: 0.08em;
48
+ text-transform: uppercase;
49
+ }
50
+ #scene-root .mk-text {
51
+ font-family: var(--mk-font-display, var(--mk-font-sans));
52
+ font-weight: 800;
53
+ line-height: 1.12;
54
+ letter-spacing: -0.02em;
55
+ text-wrap: balance;
56
+ font-variant-numeric: var(--mk-numeric, tabular-nums);
57
+ }
58
+ #scene-root .mk-formula .mk-text {
59
+ font-weight: 700;
60
+ }
61
+ #scene-root .mk-definition .mk-text,
62
+ #scene-root .mk-tip .mk-text {
63
+ font-weight: 600;
64
+ }
65
+ #scene-root .mk-size-1 .mk-text {
66
+ font-size: var(--mk-font-size-display);
67
+ }
68
+ #scene-root .mk-size-2 .mk-text {
69
+ font-size: var(--mk-font-size-title);
70
+ }
71
+ #scene-root .mk-size-3 .mk-text {
72
+ font-size: var(--mk-font-size-xl);
73
+ }
74
+ #scene-root .mk-term {
75
+ display: inline-block;
76
+ }
77
+ #scene-root .mk-op {
78
+ color: var(--mk-color-muted);
79
+ font-weight: 500;
80
+ }
81
+ #scene-root .accent {
82
+ color: var(--k);
83
+ }
84
+ #scene-root .mk-rule {
85
+ background: linear-gradient(90deg, var(--k), var(--mk-color-accent2));
86
+ transform: scaleX(0);
87
+ }
88
+ #scene-root .mk-formula .mk-rule {
89
+ transform-origin: center;
90
+ }
91
+ #scene-root .mk-note {
92
+ font-size: var(--mk-font-size-md);
93
+ font-weight: 500;
94
+ line-height: 1.3;
95
+ color: var(--mk-color-muted);
96
+ max-width: 90%;
97
+ }
@@ -0,0 +1 @@
1
+ export declare const captionScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ import { defineScene } from "../../define-scene.js";
3
+ import { animAttrs, esc } from "../../html.js";
4
+ import { readText } from "../../text-file.js";
5
+ import { limitWords, text } from "../../words.js";
6
+ /**
7
+ * One caption word on the clip clock (seconds). Same shape as the canonical
8
+ * `WordTiming` (`packages/schema/src/audio.ts`): `kit.reveal(words, t)` and
9
+ * voice word timings plug in with zero scene changes.
10
+ */
11
+ const captionWordSchema = z
12
+ .strictObject({
13
+ w: text.max(40),
14
+ start: z.number().min(0),
15
+ end: z.number().positive(),
16
+ })
17
+ .refine((word) => word.end > word.start, "end must be after start");
18
+ const captionWordsSchema = z
19
+ .array(captionWordSchema)
20
+ .min(1)
21
+ .max(40)
22
+ .superRefine((words, ctx) => {
23
+ words.forEach((word, index) => {
24
+ const prev = words[index - 1];
25
+ if (prev && word.start < prev.end) {
26
+ ctx.addIssue({
27
+ code: "custom",
28
+ path: [index, "start"],
29
+ message: `word "${word.w}" overlaps "${prev.w}"`,
30
+ });
31
+ }
32
+ });
33
+ });
34
+ const configSchema = limitWords(z
35
+ .strictObject({
36
+ text: text.max(140).optional(),
37
+ words: captionWordsSchema.optional(),
38
+ })
39
+ .superRefine((props, ctx) => {
40
+ if (props.text === undefined && props.words === undefined) {
41
+ ctx.addIssue({ code: "custom", message: "give text, words, or both" });
42
+ }
43
+ }), (props) => [...(props.text ? [props.text] : []), ...(props.words?.map((word) => word.w) ?? [])]);
44
+ const render = (props) => {
45
+ const line = props.words
46
+ ? props.words
47
+ .map((word, index) => `<span class="mk-word" data-i="${index}" data-start="${word.start}" data-end="${word.end}">${esc(word.w)}</span>`)
48
+ .join(" ")
49
+ : `<span class="mk-full">${esc(props.text ?? "")}</span>`;
50
+ return [
51
+ `<div class="mk-caption" ${animAttrs("fade", 0)}>`,
52
+ `<p class="mk-line">${line}</p>`,
53
+ "</div>",
54
+ ].join("\n");
55
+ };
56
+ export const captionScene = defineScene({
57
+ type: "caption",
58
+ layer: "overlay",
59
+ configSchema,
60
+ meta: {
61
+ purpose: "Voice word timings as karaoke captions; falls back to a full-text card.",
62
+ example: {
63
+ words: [
64
+ { w: "Hello", start: 0, end: 0.4 },
65
+ { w: "world", start: 0.4, end: 0.9 },
66
+ ],
67
+ },
68
+ },
69
+ style: readText("./style.css", import.meta.url),
70
+ script: readText("./scene.js", import.meta.url),
71
+ render: (props) => ({ body: render(props) }),
72
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Karaoke captions: each `.mk-word` carries its clip-clock window in
3
+ * `data-start`/`data-end` (kit.reveal-compatible `WordTiming` shape).
4
+ * `seek(t)` is a pure function of `t`: the live word gets `mk-now`,
5
+ * spoken words keep `mk-on`, everything else is dimmed by CSS.
6
+ * Without `words` the fragment holds a static `.mk-full` card and
7
+ * seek is a no-op envelope fade.
8
+ *
9
+ * @type {import("../../runtime/mount.js").CreateScene}
10
+ */
11
+ export declare function createScene(root: HTMLElement, config: import("../../runtime/mount.js").SceneConfig, ctx: import("../../runtime/mount.js").RuntimeContext): import("../../runtime/mount.js").Runtime;
@@ -0,0 +1,33 @@
1
+ // @ts-check
2
+ import { mountOverlay } from "../../runtime/mount.js";
3
+ /**
4
+ * Karaoke captions: each `.mk-word` carries its clip-clock window in
5
+ * `data-start`/`data-end` (kit.reveal-compatible `WordTiming` shape).
6
+ * `seek(t)` is a pure function of `t`: the live word gets `mk-now`,
7
+ * spoken words keep `mk-on`, everything else is dimmed by CSS.
8
+ * Without `words` the fragment holds a static `.mk-full` card and
9
+ * seek is a no-op envelope fade.
10
+ *
11
+ * @type {import("../../runtime/mount.js").CreateScene}
12
+ */
13
+ export function createScene(root, config, ctx) {
14
+ const words = /** @type {HTMLElement[]} */ (Array.from(root.querySelectorAll(".mk-word")));
15
+ const times = words.map((el) => ({
16
+ el,
17
+ start: Number(el.dataset.start),
18
+ end: Number(el.dataset.end),
19
+ }));
20
+ return mountOverlay(root, config, ctx, { baseDelay: 0.1, staggerFrames: 4, rise: 0 }, {
21
+ /** @param {number} t */
22
+ update: (t) => {
23
+ for (const { el, start, end } of times) {
24
+ el.classList.toggle("mk-now", t >= start && t < end);
25
+ el.classList.toggle("mk-on", t >= end);
26
+ }
27
+ },
28
+ reset: () => {
29
+ for (const { el } of times)
30
+ el.classList.remove("mk-now", "mk-on");
31
+ },
32
+ });
33
+ }
@@ -0,0 +1,33 @@
1
+ #scene-root {
2
+ justify-content: flex-end;
3
+ align-items: center;
4
+ padding-bottom: calc(var(--mk-space-xxl) + var(--mk-space-lg));
5
+ }
6
+ #scene-root .mk-caption {
7
+ align-self: center;
8
+ max-width: min(88ch, 100%);
9
+ padding: var(--mk-space-md) var(--mk-space-lg);
10
+ border-radius: var(--mk-radius-lg);
11
+ border: var(--mk-stroke-card, 2px) solid var(--mk-color-divider);
12
+ background: var(--mk-color-card);
13
+ text-align: center;
14
+ }
15
+ #scene-root .mk-line {
16
+ font-family: var(--mk-font-sans);
17
+ font-size: var(--mk-font-size-lg);
18
+ font-weight: 600;
19
+ line-height: 1.4;
20
+ text-wrap: balance;
21
+ }
22
+ #scene-root .mk-word {
23
+ color: var(--mk-color-muted);
24
+ }
25
+ #scene-root .mk-word.mk-on {
26
+ color: var(--mk-color-fg);
27
+ }
28
+ #scene-root .mk-word.mk-now {
29
+ color: var(--mk-color-accent);
30
+ }
31
+ #scene-root .mk-full {
32
+ color: var(--mk-color-fg);
33
+ }
@@ -0,0 +1 @@
1
+ export declare const chapterScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,29 @@
1
+ import { z } from "zod";
2
+ import { defineScene } from "../../define-scene.js";
3
+ import { animAttrs, esc } from "../../html.js";
4
+ import { cornerSchema, positionClass, positionCss } from "../../position.js";
5
+ import { readText } from "../../text-file.js";
6
+ import { limitWords, present, text } from "../../words.js";
7
+ const configSchema = limitWords(z.strictObject({
8
+ number: z.number().int().min(0).max(99),
9
+ label: text,
10
+ position: cornerSchema.default("top-left"),
11
+ }), (props) => present(props.number, props.label));
12
+ const render = (props) => [
13
+ `<div class="mk-chip ${positionClass(props.position)}" ${animAttrs("fade", 0)}>`,
14
+ `<span class="mk-number">${props.number}</span>`,
15
+ `<span class="mk-label">${esc(props.label)}</span>`,
16
+ "</div>",
17
+ ].join("\n");
18
+ export const chapterScene = defineScene({
19
+ type: "chapter",
20
+ layer: "overlay",
21
+ configSchema,
22
+ meta: {
23
+ purpose: "Numbered chapter chip in a corner.",
24
+ example: { number: 1, label: "Intro" },
25
+ },
26
+ style: readText("./style.css", import.meta.url),
27
+ script: readText("./scene.js", import.meta.url),
28
+ render: (props, _ctx, root) => ({ body: render(props), css: positionCss(root) }),
29
+ });
@@ -0,0 +1,2 @@
1
+ /** @type {import("../../runtime/mount.js").CreateScene} */
2
+ export declare function createScene(root: HTMLElement, config: import("../../runtime/mount.js").SceneConfig, ctx: import("../../runtime/mount.js").RuntimeContext): import("../../runtime/mount.js").Runtime;
@@ -0,0 +1,6 @@
1
+ // @ts-check
2
+ import { mountOverlay } from "../../runtime/mount.js";
3
+ /** @type {import("../../runtime/mount.js").CreateScene} */
4
+ export function createScene(root, config, ctx) {
5
+ return mountOverlay(root, config, ctx, { baseDelay: 0.1, staggerFrames: 4, rise: 24 });
6
+ }
@@ -0,0 +1,25 @@
1
+ #scene-root .mk-chip {
2
+ font-family: var(--mk-font-display, var(--mk-font-sans));
3
+ display: flex;
4
+ align-items: stretch;
5
+ overflow: hidden;
6
+ border-radius: var(--mk-radius-full);
7
+ border: var(--mk-stroke-card, 2px) solid var(--mk-color-divider);
8
+ background: var(--mk-color-card);
9
+ font-weight: 700;
10
+ }
11
+ #scene-root .mk-number {
12
+ display: grid;
13
+ place-items: center;
14
+ min-width: var(--mk-space-xxl);
15
+ padding: var(--mk-space-sm) var(--mk-space-md);
16
+ background: var(--mk-color-accent);
17
+ color: var(--mk-color-onAccent);
18
+ font-variant-numeric: var(--mk-numeric, tabular-nums);
19
+ }
20
+ #scene-root .mk-label {
21
+ display: grid;
22
+ align-items: center;
23
+ padding: var(--mk-space-sm) var(--mk-space-lg);
24
+ color: var(--mk-color-fg);
25
+ }
@@ -0,0 +1 @@
1
+ export declare const chartScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,24 @@
1
+ import { defineScene } from "../../define-scene.js";
2
+ import { readText } from "../../text-file.js";
3
+ import { render } from "./render.js";
4
+ import { configSchema } from "./schema.js";
5
+ export const chartScene = defineScene({
6
+ type: "chart",
7
+ configSchema,
8
+ meta: {
9
+ purpose: "Animated bar, line or donut chart for ranking, trend or share, with values shown and an optional highlight.",
10
+ example: {
11
+ kind: "bar",
12
+ title: "Signups by channel",
13
+ data: [
14
+ { label: "Search", value: 420 },
15
+ { label: "Referral", value: 310 },
16
+ { label: "Social", value: 190 },
17
+ ],
18
+ highlight: 0,
19
+ },
20
+ },
21
+ style: readText("./style.css", import.meta.url),
22
+ script: readText("./scene.js", import.meta.url),
23
+ render,
24
+ });
@@ -0,0 +1,4 @@
1
+ import type { EmitContext } from "@anvpx/motion-schema";
2
+ import type { Rendered } from "../../define-scene.ts";
3
+ import type { Props } from "./schema.ts";
4
+ export declare const render: (props: Props, ctx: EmitContext, root: string) => Rendered;