@anvpx/motion-scenes 0.0.0-stage → 0.1.1

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 (221) 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 +176 -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/compose.d.ts +21 -0
  18. package/dist/runtime/compose.js +43 -0
  19. package/dist/runtime/easing.d.ts +14 -0
  20. package/dist/runtime/easing.js +45 -0
  21. package/dist/runtime/mount.d.ts +50 -0
  22. package/dist/runtime/mount.js +153 -0
  23. package/dist/runtime/timeline.d.ts +147 -0
  24. package/dist/runtime/timeline.js +199 -0
  25. package/dist/runtime-files.d.ts +4 -0
  26. package/dist/runtime-files.js +18 -0
  27. package/dist/scene-contract.d.ts +7 -0
  28. package/dist/scene-contract.js +104 -0
  29. package/dist/scene3d/post.d.ts +12 -0
  30. package/dist/scene3d/post.js +16 -0
  31. package/dist/scene3d/reflector.d.ts +9 -0
  32. package/dist/scene3d/reflector.js +17 -0
  33. package/dist/scene3d/textures.d.ts +9 -0
  34. package/dist/scene3d/textures.js +11 -0
  35. package/dist/scene3d/volumetric.d.ts +9 -0
  36. package/dist/scene3d/volumetric.js +16 -0
  37. package/dist/scenes/bug/index.d.ts +1 -0
  38. package/dist/scenes/bug/index.js +45 -0
  39. package/dist/scenes/bug/scene.d.ts +2 -0
  40. package/dist/scenes/bug/scene.js +6 -0
  41. package/dist/scenes/bug/style.css +30 -0
  42. package/dist/scenes/bullets/index.d.ts +1 -0
  43. package/dist/scenes/bullets/index.js +128 -0
  44. package/dist/scenes/bullets/scene.d.ts +2 -0
  45. package/dist/scenes/bullets/scene.js +6 -0
  46. package/dist/scenes/bullets/style.css +91 -0
  47. package/dist/scenes/callout/index.d.ts +9 -0
  48. package/dist/scenes/callout/index.js +95 -0
  49. package/dist/scenes/callout/scene.d.ts +2 -0
  50. package/dist/scenes/callout/scene.js +43 -0
  51. package/dist/scenes/callout/style.css +97 -0
  52. package/dist/scenes/caption/index.d.ts +1 -0
  53. package/dist/scenes/caption/index.js +72 -0
  54. package/dist/scenes/caption/scene.d.ts +11 -0
  55. package/dist/scenes/caption/scene.js +33 -0
  56. package/dist/scenes/caption/style.css +33 -0
  57. package/dist/scenes/chapter/index.d.ts +1 -0
  58. package/dist/scenes/chapter/index.js +29 -0
  59. package/dist/scenes/chapter/scene.d.ts +2 -0
  60. package/dist/scenes/chapter/scene.js +6 -0
  61. package/dist/scenes/chapter/style.css +25 -0
  62. package/dist/scenes/chart/index.d.ts +1 -0
  63. package/dist/scenes/chart/index.js +24 -0
  64. package/dist/scenes/chart/render.d.ts +4 -0
  65. package/dist/scenes/chart/render.js +257 -0
  66. package/dist/scenes/chart/scale.d.ts +11 -0
  67. package/dist/scenes/chart/scale.js +53 -0
  68. package/dist/scenes/chart/scene.d.ts +2 -0
  69. package/dist/scenes/chart/scene.js +73 -0
  70. package/dist/scenes/chart/schema.d.ts +36 -0
  71. package/dist/scenes/chart/schema.js +109 -0
  72. package/dist/scenes/chart/style.css +425 -0
  73. package/dist/scenes/code/index.d.ts +1 -0
  74. package/dist/scenes/code/index.js +109 -0
  75. package/dist/scenes/code/scene.d.ts +2 -0
  76. package/dist/scenes/code/scene.js +108 -0
  77. package/dist/scenes/code/style.css +155 -0
  78. package/dist/scenes/code/tokenize.d.ts +8 -0
  79. package/dist/scenes/code/tokenize.js +145 -0
  80. package/dist/scenes/compare/index.d.ts +1 -0
  81. package/dist/scenes/compare/index.js +128 -0
  82. package/dist/scenes/compare/scene.d.ts +2 -0
  83. package/dist/scenes/compare/scene.js +58 -0
  84. package/dist/scenes/compare/style.css +187 -0
  85. package/dist/scenes/countdown/index.d.ts +1 -0
  86. package/dist/scenes/countdown/index.js +46 -0
  87. package/dist/scenes/countdown/scene.d.ts +2 -0
  88. package/dist/scenes/countdown/scene.js +21 -0
  89. package/dist/scenes/countdown/style.css +32 -0
  90. package/dist/scenes/flow/index.d.ts +1 -0
  91. package/dist/scenes/flow/index.js +66 -0
  92. package/dist/scenes/flow/inspect.d.ts +26 -0
  93. package/dist/scenes/flow/inspect.js +25 -0
  94. package/dist/scenes/flow/layout.d.ts +83 -0
  95. package/dist/scenes/flow/layout.js +222 -0
  96. package/dist/scenes/flow/measure.d.ts +15 -0
  97. package/dist/scenes/flow/measure.js +33 -0
  98. package/dist/scenes/flow/plan.d.ts +42 -0
  99. package/dist/scenes/flow/plan.js +117 -0
  100. package/dist/scenes/flow/scene.d.ts +2 -0
  101. package/dist/scenes/flow/scene.js +6 -0
  102. package/dist/scenes/flow/schema.d.ts +49 -0
  103. package/dist/scenes/flow/schema.js +60 -0
  104. package/dist/scenes/flow/style.css +54 -0
  105. package/dist/scenes/hero/index.d.ts +3 -0
  106. package/dist/scenes/hero/index.js +77 -0
  107. package/dist/scenes/hero/scene.d.ts +2 -0
  108. package/dist/scenes/hero/scene.js +6 -0
  109. package/dist/scenes/hero/style.css +143 -0
  110. package/dist/scenes/icon-grid/index.d.ts +1 -0
  111. package/dist/scenes/icon-grid/index.js +69 -0
  112. package/dist/scenes/icon-grid/scene.d.ts +2 -0
  113. package/dist/scenes/icon-grid/scene.js +6 -0
  114. package/dist/scenes/icon-grid/style.css +74 -0
  115. package/dist/scenes/kinetic/index.d.ts +1 -0
  116. package/dist/scenes/kinetic/index.js +62 -0
  117. package/dist/scenes/kinetic/scene.d.ts +2 -0
  118. package/dist/scenes/kinetic/scene.js +98 -0
  119. package/dist/scenes/kinetic/style.css +94 -0
  120. package/dist/scenes/kpi-grid/index.d.ts +1 -0
  121. package/dist/scenes/kpi-grid/index.js +161 -0
  122. package/dist/scenes/kpi-grid/scene.d.ts +2 -0
  123. package/dist/scenes/kpi-grid/scene.js +6 -0
  124. package/dist/scenes/kpi-grid/style.css +141 -0
  125. package/dist/scenes/lower-third/index.d.ts +1 -0
  126. package/dist/scenes/lower-third/index.js +33 -0
  127. package/dist/scenes/lower-third/scene.d.ts +2 -0
  128. package/dist/scenes/lower-third/scene.js +6 -0
  129. package/dist/scenes/lower-third/style.css +45 -0
  130. package/dist/scenes/media-frame/index.d.ts +1 -0
  131. package/dist/scenes/media-frame/index.js +99 -0
  132. package/dist/scenes/media-frame/scene.d.ts +2 -0
  133. package/dist/scenes/media-frame/scene.js +85 -0
  134. package/dist/scenes/media-frame/style.css +275 -0
  135. package/dist/scenes/outro/index.d.ts +1 -0
  136. package/dist/scenes/outro/index.js +54 -0
  137. package/dist/scenes/outro/scene.d.ts +2 -0
  138. package/dist/scenes/outro/scene.js +6 -0
  139. package/dist/scenes/outro/style.css +92 -0
  140. package/dist/scenes/poster/index.d.ts +3 -0
  141. package/dist/scenes/poster/index.js +62 -0
  142. package/dist/scenes/poster/scene.d.ts +2 -0
  143. package/dist/scenes/poster/scene.js +6 -0
  144. package/dist/scenes/poster/style.css +102 -0
  145. package/dist/scenes/progress/index.d.ts +1 -0
  146. package/dist/scenes/progress/index.js +40 -0
  147. package/dist/scenes/progress/scene.d.ts +2 -0
  148. package/dist/scenes/progress/scene.js +17 -0
  149. package/dist/scenes/progress/style.css +31 -0
  150. package/dist/scenes/quote/index.d.ts +3 -0
  151. package/dist/scenes/quote/index.js +69 -0
  152. package/dist/scenes/quote/scene.d.ts +2 -0
  153. package/dist/scenes/quote/scene.js +6 -0
  154. package/dist/scenes/quote/style.css +86 -0
  155. package/dist/scenes/scene3d/index.d.ts +1 -0
  156. package/dist/scenes/scene3d/index.js +59 -0
  157. package/dist/scenes/scene3d/scene.d.ts +2 -0
  158. package/dist/scenes/scene3d/scene.js +436 -0
  159. package/dist/scenes/scene3d/style.css +43 -0
  160. package/dist/scenes/stat/index.d.ts +4 -0
  161. package/dist/scenes/stat/index.js +72 -0
  162. package/dist/scenes/stat/scene.d.ts +2 -0
  163. package/dist/scenes/stat/scene.js +6 -0
  164. package/dist/scenes/stat/style.css +95 -0
  165. package/dist/scenes/steps/index.d.ts +1 -0
  166. package/dist/scenes/steps/index.js +147 -0
  167. package/dist/scenes/steps/scene.d.ts +2 -0
  168. package/dist/scenes/steps/scene.js +6 -0
  169. package/dist/scenes/steps/style.css +190 -0
  170. package/dist/scenes/sticker/index.d.ts +1 -0
  171. package/dist/scenes/sticker/index.js +48 -0
  172. package/dist/scenes/sticker/scene.d.ts +2 -0
  173. package/dist/scenes/sticker/scene.js +57 -0
  174. package/dist/scenes/sticker/style.css +88 -0
  175. package/dist/scenes/table/index.d.ts +1 -0
  176. package/dist/scenes/table/index.js +131 -0
  177. package/dist/scenes/table/scene.d.ts +2 -0
  178. package/dist/scenes/table/scene.js +6 -0
  179. package/dist/scenes/table/style.css +102 -0
  180. package/dist/scenes/timeline/index.d.ts +1 -0
  181. package/dist/scenes/timeline/index.js +68 -0
  182. package/dist/scenes/timeline/scene.d.ts +2 -0
  183. package/dist/scenes/timeline/scene.js +6 -0
  184. package/dist/scenes/timeline/style.css +161 -0
  185. package/dist/scenes/title/index.d.ts +1 -0
  186. package/dist/scenes/title/index.js +52 -0
  187. package/dist/scenes/title/scene.d.ts +2 -0
  188. package/dist/scenes/title/scene.js +6 -0
  189. package/dist/scenes/title/style.css +80 -0
  190. package/dist/scenes/waveform/index.d.ts +1 -0
  191. package/dist/scenes/waveform/index.js +39 -0
  192. package/dist/scenes/waveform/scene.d.ts +2 -0
  193. package/dist/scenes/waveform/scene.js +35 -0
  194. package/dist/scenes/waveform/style.css +44 -0
  195. package/dist/shared/fit-text.d.ts +24 -0
  196. package/dist/shared/fit-text.js +82 -0
  197. package/dist/shared/format.d.ts +24 -0
  198. package/dist/shared/format.js +59 -0
  199. package/dist/shared/frame.d.ts +14 -0
  200. package/dist/shared/frame.js +21 -0
  201. package/dist/shared/icon-search.d.ts +9 -0
  202. package/dist/shared/icon-search.js +26 -0
  203. package/dist/shared/icon.d.ts +15 -0
  204. package/dist/shared/icon.js +28 -0
  205. package/dist/shared/index.d.ts +11 -0
  206. package/dist/shared/index.js +7 -0
  207. package/dist/shared/markup.d.ts +12 -0
  208. package/dist/shared/markup.js +43 -0
  209. package/dist/shared/measure.d.ts +7 -0
  210. package/dist/shared/measure.js +34 -0
  211. package/dist/shared/motion.d.ts +94 -0
  212. package/dist/shared/motion.js +117 -0
  213. package/dist/source-hash.d.ts +4 -0
  214. package/dist/source-hash.js +26 -0
  215. package/dist/test-fixtures.d.ts +9 -0
  216. package/dist/test-fixtures.js +22 -0
  217. package/dist/text-file.d.ts +1 -0
  218. package/dist/text-file.js +3 -0
  219. package/dist/words.d.ts +11 -0
  220. package/dist/words.js +93 -0
  221. package/package.json +36 -3
@@ -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 { mountTimeline } from "../../runtime/mount.js";
3
+ /** @type {import("../../runtime/mount.js").CreateScene} */
4
+ export function createScene(root, config, ctx) {
5
+ return mountTimeline(root, config, ctx, { baseDelay: 0.2, staggerFrames: 4, rise: 28 });
6
+ }
@@ -0,0 +1,102 @@
1
+ #scene-root {
2
+ gap: var(--mk-space-lg);
3
+ justify-content: flex-start;
4
+ }
5
+ #scene-root .mk-head {
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: flex-start;
9
+ gap: var(--mk-space-md);
10
+ }
11
+ #scene-root .mk-heading {
12
+ font-family: var(--mk-font-display, var(--mk-font-sans));
13
+ font-size: var(--mk-font-size-xl);
14
+ font-weight: 800;
15
+ line-height: 1.04;
16
+ letter-spacing: -0.03em;
17
+ text-wrap: balance;
18
+ }
19
+ #scene-root .mk-table {
20
+ flex: 0 1 auto;
21
+ min-height: 0;
22
+ display: flex;
23
+ flex-direction: column;
24
+ overflow: hidden;
25
+ background: var(--mk-color-card);
26
+ border: var(--mk-stroke-card, 2px) solid var(--mk-color-divider);
27
+ border-radius: var(--mk-radius-xl);
28
+ box-shadow: var(
29
+ --mk-shadow-card,
30
+ 0 24px 56px -28px color-mix(in srgb, var(--mk-color-fg) 40%, transparent)
31
+ );
32
+ font-variant-numeric: var(--mk-numeric, tabular-nums);
33
+ }
34
+ #scene-root .mk-trow {
35
+ flex: 1 1 auto;
36
+ min-height: 88px;
37
+ display: grid;
38
+ align-items: center;
39
+ column-gap: var(--mk-space-lg);
40
+ padding: var(--mk-space-md) var(--mk-space-xl);
41
+ border-top: 2px solid var(--mk-color-divider);
42
+ font-size: var(--mk-font-size-md);
43
+ font-weight: 600;
44
+ line-height: 1.2;
45
+ }
46
+ #scene-root .mk-thead {
47
+ flex: none;
48
+ border-top: 0;
49
+ background: var(--mk-color-surface);
50
+ font-size: var(--mk-font-size-sm);
51
+ font-weight: 700;
52
+ letter-spacing: 0.04em;
53
+ text-transform: uppercase;
54
+ color: var(--mk-color-muted);
55
+ }
56
+ #scene-root .mk-cell {
57
+ min-width: 0;
58
+ text-wrap: balance;
59
+ }
60
+ #scene-root .mk-l {
61
+ text-align: left;
62
+ }
63
+ #scene-root .mk-r {
64
+ text-align: right;
65
+ white-space: nowrap;
66
+ }
67
+ #scene-root .mk-rankh {
68
+ text-align: center;
69
+ }
70
+ #scene-root .mk-rank {
71
+ display: grid;
72
+ place-items: center;
73
+ }
74
+ #scene-root .mk-rank b {
75
+ display: grid;
76
+ place-items: center;
77
+ width: 56px;
78
+ height: 56px;
79
+ border-radius: var(--mk-radius-full);
80
+ background: var(--mk-color-surface);
81
+ font-weight: 800;
82
+ }
83
+ #scene-root .mk-rank.mk-first b {
84
+ background: var(--mk-color-accent);
85
+ color: var(--mk-color-onAccent);
86
+ }
87
+ #scene-root .mk-hi {
88
+ background: var(--mk-color-accentSoft);
89
+ box-shadow: inset 10px 0 0 var(--mk-color-accent);
90
+ font-weight: 800;
91
+ }
92
+ #scene-root .mk-roomy .mk-trow:not(.mk-thead) {
93
+ font-size: var(--mk-font-size-lg);
94
+ min-height: 132px;
95
+ }
96
+ #scene-root .mk-roomy .mk-thead {
97
+ font-size: var(--mk-font-size-md);
98
+ }
99
+ #scene-root .mk-roomy .mk-rank b {
100
+ width: 72px;
101
+ height: 72px;
102
+ }
@@ -0,0 +1 @@
1
+ export declare const timelineScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,68 @@
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 { readText } from "../../text-file.js";
6
+ import { limitWords, present, text } from "../../words.js";
7
+ const COUNT_MESSAGE = "need 2-7 items";
8
+ const itemSchema = z.strictObject({
9
+ when: text,
10
+ label: text,
11
+ detail: text.optional(),
12
+ state: z.enum(["done", "now", "next"]).optional(),
13
+ });
14
+ const configSchema = limitWords(z.strictObject({
15
+ title: text.optional(),
16
+ items: z.array(itemSchema).min(2, COUNT_MESSAGE).max(7, COUNT_MESSAGE),
17
+ orient: z.enum(["h", "v"]).default("h"),
18
+ }), (props) => props.items
19
+ .flatMap((item) => present(item.when, item.label, item.detail))
20
+ .concat(present(props.title)));
21
+ /** State is shown by shape (check / ringed dot / hollow dot), never by colour alone. */
22
+ const node = (item, index) => {
23
+ const state = item.state ?? "plain";
24
+ const inner = item.state === "done" ? sceneIcon("check", { at: `props.items[${index}]` }) : "";
25
+ return `<span class="mk-node mk-state-${state}">${inner}</span>`;
26
+ };
27
+ const entry = (item, index, offset) => {
28
+ const detail = item.detail ? `\n<p class="mk-detail">${esc(item.detail)}</p>` : "";
29
+ const attrs = animAttrs("rise", index + offset, `item-${index + 1}`);
30
+ return `<li class="mk-item mk-state-${item.state ?? "plain"}" ${attrs}>
31
+ <p class="mk-when">${esc(item.when)}</p>
32
+ ${node(item, index)}
33
+ <div class="mk-body">
34
+ <p class="mk-label">${esc(item.label)}</p>${detail}
35
+ </div>
36
+ </li>`;
37
+ };
38
+ const render = (props) => {
39
+ const offset = props.title ? 1 : 0;
40
+ const head = props.title
41
+ ? `<div class="mk-head">
42
+ <h1 class="mk-heading" ${animAttrs("rise", 0, "title")}>${esc(props.title)}</h1>
43
+ <span class="mk-rule" ${animAttrs("grow", 0, "title")}></span>
44
+ </div>
45
+ `
46
+ : "";
47
+ const items = props.items.map((item, index) => entry(item, index, offset));
48
+ const cls = `mk-track mk-${props.orient} mk-n${props.items.length}`;
49
+ return `${head}<ol class="${cls}">\n${items.join("\n")}\n</ol>`;
50
+ };
51
+ export const timelineScene = defineScene({
52
+ type: "timeline",
53
+ configSchema,
54
+ meta: {
55
+ purpose: "Dated milestones, history or a now/next roadmap: 2-7 items on a horizontal or vertical axis with done/now/next states.",
56
+ example: {
57
+ title: "Roadmap",
58
+ items: [
59
+ { when: "Q1", label: "Beta", state: "done" },
60
+ { when: "Q2", label: "Launch", state: "now" },
61
+ { when: "Q3", label: "Scale", state: "next" },
62
+ ],
63
+ },
64
+ },
65
+ style: readText("./style.css", import.meta.url),
66
+ script: readText("./scene.js", import.meta.url),
67
+ render: (props) => ({ body: render(props) }),
68
+ });
@@ -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 { mountTimeline } from "../../runtime/mount.js";
3
+ /** @type {import("../../runtime/mount.js").CreateScene} */
4
+ export function createScene(root, config, ctx) {
5
+ return mountTimeline(root, config, ctx, { baseDelay: 0.2, staggerFrames: 4, rise: 36 });
6
+ }
@@ -0,0 +1,161 @@
1
+ #scene-root {
2
+ gap: var(--mk-space-xxl);
3
+ }
4
+ #scene-root .mk-head {
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: flex-start;
8
+ gap: var(--mk-space-md);
9
+ }
10
+ #scene-root .mk-heading {
11
+ font-family: var(--mk-font-display, var(--mk-font-sans));
12
+ font-size: var(--mk-font-size-title);
13
+ font-weight: 800;
14
+ line-height: 1.04;
15
+ letter-spacing: -0.03em;
16
+ text-wrap: balance;
17
+ }
18
+ #scene-root:has(.mk-v) {
19
+ flex-direction: row;
20
+ justify-content: flex-start;
21
+ align-items: center;
22
+ gap: var(--mk-space-xxl);
23
+ }
24
+ #scene-root:has(.mk-v) .mk-head {
25
+ flex: 0 0 30%;
26
+ }
27
+ #scene-root .mk-track {
28
+ --node: 72px;
29
+ --when: var(--mk-font-size-lg);
30
+ list-style: none;
31
+ padding: 0;
32
+ }
33
+ #scene-root .mk-n6,
34
+ #scene-root .mk-n7 {
35
+ --node: 56px;
36
+ --when: var(--mk-font-size-md);
37
+ }
38
+ #scene-root .mk-when {
39
+ font-size: var(--mk-font-size-lg);
40
+ font-weight: 800;
41
+ letter-spacing: 0.02em;
42
+ color: var(--mk-color-accent);
43
+ }
44
+ #scene-root .mk-label {
45
+ font-size: var(--mk-font-size-lg);
46
+ font-weight: 700;
47
+ line-height: 1.15;
48
+ text-wrap: balance;
49
+ overflow-wrap: break-word;
50
+ }
51
+ #scene-root .mk-detail {
52
+ margin-top: var(--mk-space-xs);
53
+ font-size: var(--mk-font-size-sm);
54
+ line-height: 1.3;
55
+ color: var(--mk-color-muted);
56
+ text-wrap: pretty;
57
+ }
58
+ #scene-root .mk-n5 .mk-label {
59
+ font-size: var(--mk-font-size-md);
60
+ }
61
+ #scene-root .mk-n6 .mk-when,
62
+ #scene-root .mk-n7 .mk-when,
63
+ #scene-root .mk-n6 .mk-label,
64
+ #scene-root .mk-n7 .mk-label {
65
+ font-size: var(--mk-font-size-md);
66
+ }
67
+ #scene-root .mk-node {
68
+ position: relative;
69
+ z-index: 1;
70
+ display: grid;
71
+ place-items: center;
72
+ flex: none;
73
+ width: var(--node);
74
+ height: var(--node);
75
+ border-radius: var(--mk-radius-full);
76
+ background: var(--mk-color-accent);
77
+ color: var(--mk-color-onAccent);
78
+ }
79
+ #scene-root .mk-node svg {
80
+ width: 60%;
81
+ height: 60%;
82
+ }
83
+ #scene-root .mk-node.mk-state-now {
84
+ box-shadow:
85
+ 0 0 0 8px var(--mk-color-bg),
86
+ 0 0 0 16px var(--mk-color-accent);
87
+ }
88
+ #scene-root .mk-node.mk-state-next {
89
+ background: var(--mk-color-card);
90
+ border: 6px solid var(--mk-color-divider);
91
+ }
92
+ #scene-root .mk-item.mk-state-next .mk-label,
93
+ #scene-root .mk-item.mk-state-next .mk-when {
94
+ color: var(--mk-color-muted);
95
+ }
96
+
97
+ /* horizontal: dates above, axis, labels below */
98
+ #scene-root .mk-h {
99
+ display: flex;
100
+ gap: var(--mk-space-md);
101
+ }
102
+ #scene-root .mk-h .mk-item {
103
+ position: relative;
104
+ flex: 1;
105
+ min-width: 0;
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: var(--mk-space-md);
109
+ }
110
+ #scene-root .mk-h .mk-item::before {
111
+ content: "";
112
+ position: absolute;
113
+ top: calc(var(--when) * 1.2 + var(--mk-space-md) + var(--node) / 2 - 4px);
114
+ left: calc(var(--node) / 2);
115
+ width: calc(100% + var(--mk-space-md));
116
+ height: 8px;
117
+ border-radius: var(--mk-radius-full);
118
+ background: var(--mk-color-accent);
119
+ }
120
+ #scene-root .mk-h .mk-item.mk-state-now::before,
121
+ #scene-root .mk-h .mk-item.mk-state-next::before {
122
+ background: var(--mk-color-divider);
123
+ }
124
+ #scene-root .mk-h .mk-item:last-child::before {
125
+ display: none;
126
+ }
127
+ #scene-root .mk-h .mk-body {
128
+ padding-right: var(--mk-space-sm);
129
+ }
130
+
131
+ /* vertical: date | node on a rail | label */
132
+ #scene-root .mk-v {
133
+ flex: 1;
134
+ display: flex;
135
+ flex-direction: column;
136
+ gap: var(--mk-space-lg);
137
+ }
138
+ #scene-root .mk-v .mk-item {
139
+ position: relative;
140
+ display: grid;
141
+ grid-template-columns: 200px var(--node) 1fr;
142
+ align-items: center;
143
+ column-gap: var(--mk-space-lg);
144
+ }
145
+ #scene-root .mk-v .mk-item .mk-when {
146
+ text-align: right;
147
+ }
148
+ #scene-root .mk-v .mk-item:not(:last-child)::after {
149
+ content: "";
150
+ position: absolute;
151
+ left: calc(200px + var(--mk-space-lg) + var(--node) / 2 - 4px);
152
+ top: 50%;
153
+ width: 8px;
154
+ height: calc(100% + var(--mk-space-lg));
155
+ border-radius: var(--mk-radius-full);
156
+ background: var(--mk-color-accent);
157
+ }
158
+ #scene-root .mk-v .mk-item.mk-state-now::after,
159
+ #scene-root .mk-v .mk-item.mk-state-next::after {
160
+ background: var(--mk-color-divider);
161
+ }
@@ -0,0 +1 @@
1
+ export declare const titleScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import { defineScene } from "../../define-scene.js";
3
+ import { animAttrs, esc } from "../../html.js";
4
+ import { frameCss } from "../../shared/frame.js";
5
+ import { readText } from "../../text-file.js";
6
+ import { limitWords, present, text } from "../../words.js";
7
+ const configSchema = limitWords(z.strictObject({ title: text, subtitle: text.optional(), kicker: text.optional() }), (props) => present(props.kicker, props.title, props.subtitle));
8
+ const ART = [
9
+ '<div class="mk-art" aria-hidden="true">',
10
+ '<i class="mk-ring mk-ring-1"></i>',
11
+ '<i class="mk-ring mk-ring-2"></i>',
12
+ '<i class="mk-ring mk-ring-3"></i>',
13
+ '<i class="mk-disc"></i>',
14
+ '<i class="mk-dot"></i>',
15
+ "</div>",
16
+ ].join("\n");
17
+ /** Tall frames: text first; the orb sits below/behind it and never under a word. */
18
+ const TALL_CSS = `#scene-root { justify-content: flex-start; }
19
+ #scene-root .mk-stack { max-width: 100%; }
20
+ #scene-root .mk-art { top: auto; margin-top: 0; width: 1000px; height: 1000px; }`;
21
+ const PORTRAIT_CSS = `#scene-root { padding-top: 16%; }
22
+ #scene-root .mk-art { right: -30%; bottom: -300px; }`;
23
+ const SQUARE_CSS = `#scene-root { padding-top: 7%; }
24
+ #scene-root .mk-art { right: -4%; bottom: -260px; transform: translateX(calc(var(--drift, 0) * -14px)) scale(0.62); transform-origin: right bottom; }`;
25
+ const render = (props) => {
26
+ const lines = [];
27
+ const add = (tag, cls, value, key) => {
28
+ const attrs = animAttrs("rise", lines.length, key);
29
+ lines.push(`<${tag} class="${cls}" ${attrs}>${esc(value)}</${tag}>`);
30
+ };
31
+ if (props.kicker)
32
+ add("p", "mk-kicker", props.kicker, "kicker");
33
+ add("h1", "mk-heading", props.title, "title");
34
+ lines.push(`<span class="mk-rule" ${animAttrs("grow", lines.length, "title")}></span>`);
35
+ if (props.subtitle)
36
+ add("p", "mk-sub", props.subtitle, "subtitle");
37
+ return `${ART}\n<div class="mk-stack">\n${lines.join("\n")}\n</div>`;
38
+ };
39
+ export const titleScene = defineScene({
40
+ type: "title",
41
+ configSchema,
42
+ meta: {
43
+ purpose: "Opening or section title card with optional kicker and subtitle.",
44
+ example: { title: "Hello" },
45
+ },
46
+ style: readText("./style.css", import.meta.url),
47
+ script: readText("./scene.js", import.meta.url),
48
+ render: (props, ctx, root) => ({
49
+ body: render(props),
50
+ css: frameCss(ctx, root, { tall: TALL_CSS, portrait: PORTRAIT_CSS, square: SQUARE_CSS }),
51
+ }),
52
+ });
@@ -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 { mountTimeline } from "../../runtime/mount.js";
3
+ /** @type {import("../../runtime/mount.js").CreateScene} */
4
+ export function createScene(root, config, ctx) {
5
+ return mountTimeline(root, config, ctx, { baseDelay: 0.3, staggerFrames: 6, rise: 48 });
6
+ }
@@ -0,0 +1,80 @@
1
+ #scene-root .mk-stack {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: flex-start;
5
+ gap: var(--mk-space-lg);
6
+ max-width: 56%;
7
+ }
8
+ #scene-root .mk-kicker {
9
+ display: flex;
10
+ align-items: center;
11
+ gap: var(--mk-space-md);
12
+ font-size: var(--mk-font-size-md);
13
+ font-weight: 700;
14
+ letter-spacing: 0.2em;
15
+ text-transform: uppercase;
16
+ color: var(--mk-color-accent);
17
+ }
18
+ #scene-root .mk-kicker::before {
19
+ content: "";
20
+ width: 72px;
21
+ height: 4px;
22
+ border-radius: var(--mk-radius-full);
23
+ background: var(--mk-color-accent);
24
+ }
25
+ #scene-root .mk-heading {
26
+ font-family: var(--mk-font-display, var(--mk-font-sans));
27
+ font-size: var(--mk-font-size-display);
28
+ font-weight: 800;
29
+ line-height: 1.02;
30
+ letter-spacing: -0.035em;
31
+ text-wrap: balance;
32
+ }
33
+ #scene-root .mk-sub {
34
+ max-width: 24em;
35
+ font-size: var(--mk-font-size-lg);
36
+ line-height: 1.3;
37
+ color: var(--mk-color-muted);
38
+ }
39
+ #scene-root .mk-art {
40
+ position: absolute;
41
+ z-index: -1;
42
+ right: -9%;
43
+ top: 50%;
44
+ width: 1000px;
45
+ height: 1000px;
46
+ margin-top: -500px;
47
+ transform: translateX(calc(var(--drift, 0) * -14px));
48
+ pointer-events: none;
49
+ }
50
+ #scene-root .mk-ring {
51
+ position: absolute;
52
+ border-radius: 50%;
53
+ border: 2px solid var(--mk-color-divider);
54
+ }
55
+ #scene-root .mk-ring-1 {
56
+ inset: 0;
57
+ }
58
+ #scene-root .mk-ring-2 {
59
+ inset: 130px;
60
+ }
61
+ #scene-root .mk-ring-3 {
62
+ inset: 260px;
63
+ border-color: var(--mk-color-accent);
64
+ }
65
+ #scene-root .mk-disc {
66
+ position: absolute;
67
+ inset: 350px;
68
+ border-radius: 50%;
69
+ background: linear-gradient(145deg, var(--mk-color-accent), var(--mk-color-deep));
70
+ box-shadow: 0 40px 90px -30px color-mix(in srgb, var(--mk-color-accent) 70%, transparent);
71
+ }
72
+ #scene-root .mk-dot {
73
+ position: absolute;
74
+ left: 118px;
75
+ top: 276px;
76
+ width: 36px;
77
+ height: 36px;
78
+ border-radius: 50%;
79
+ background: var(--mk-color-accent2);
80
+ }
@@ -0,0 +1 @@
1
+ export declare const waveformScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { defineScene } from "../../define-scene.js";
3
+ import { esc } from "../../html.js";
4
+ import { readText } from "../../text-file.js";
5
+ import { limitWords, text } from "../../words.js";
6
+ const MIN_PEAKS = 16;
7
+ const MAX_PEAKS = 128;
8
+ const configSchema = limitWords(z.strictObject({
9
+ peaks: z
10
+ .array(z.number().min(0).max(1))
11
+ .min(MIN_PEAKS, `need ${MIN_PEAKS}-${MAX_PEAKS} peaks`)
12
+ .max(MAX_PEAKS, `need ${MIN_PEAKS}-${MAX_PEAKS} peaks`),
13
+ label: text.max(40).optional(),
14
+ }), (props) => (props.label === undefined ? [] : [props.label]));
15
+ const render = (props, _ctx) => {
16
+ const bars = props.peaks
17
+ .map((peak, index) => `<i class="mk-bar" data-i="${index}" data-h="${peak}"></i>`)
18
+ .join("");
19
+ const label = props.label === undefined ? "" : `<p class="mk-label">${esc(props.label)}</p>`;
20
+ return `<div class="mk-stage">
21
+ <div class="mk-wave" role="img" aria-label="${esc(props.label ?? "Audio waveform")}">${bars}</div>
22
+ <div class="mk-playhead" aria-hidden="true"></div>
23
+ ${label}
24
+ </div>`;
25
+ };
26
+ export const waveformScene = defineScene({
27
+ type: "waveform",
28
+ configSchema,
29
+ meta: {
30
+ purpose: "Audio waveform from precomputed peaks: bars rise as the playhead sweeps; for voice agents, podcasts and narrated reports. Peaks are storyboard data, never decoded audio.",
31
+ example: {
32
+ peaks: [0.2, 0.4, 0.6, 0.8, 0.6, 0.4, 0.3, 0.5, 0.7, 0.9, 0.7, 0.5, 0.4, 0.3, 0.2, 0.1],
33
+ label: "Intro",
34
+ },
35
+ },
36
+ style: readText("./style.css", import.meta.url),
37
+ script: readText("./scene.js", import.meta.url),
38
+ render: (props, ctx) => ({ body: render(props, ctx) }),
39
+ });
@@ -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,35 @@
1
+ // @ts-check
2
+ import { clamp01 } from "../../runtime/easing.js";
3
+ import { mountTimeline } from "../../runtime/mount.js";
4
+ /** @type {import("../../runtime/mount.js").CreateScene} */
5
+ export function createScene(root, config, ctx) {
6
+ const inner = mountTimeline(root, config, ctx, { baseDelay: 0.1, staggerFrames: 4, rise: 0 });
7
+ const wave = /** @type {HTMLElement} */ (root.querySelector(".mk-wave"));
8
+ const bars = /** @type {HTMLElement[]} */ (Array.from(wave.children));
9
+ const playhead = /** @type {HTMLElement} */ (root.querySelector(".mk-playhead"));
10
+ const duration = ctx.duration ?? (Number(root.dataset.duration) || 6);
11
+ const count = bars.length;
12
+ /** @param {number} t */
13
+ const seek = (t) => {
14
+ inner.seek(t);
15
+ const progress = duration > 0 ? clamp01(t / duration) : 1;
16
+ const head = progress * count;
17
+ bars.forEach((bar, i) => {
18
+ const peak = Number(bar.dataset.h ?? 0);
19
+ const seen = clamp01(head - i);
20
+ bar.style.height = `${Math.max(4, peak * 100 * seen)}%`;
21
+ bar.style.opacity = String(0.35 + 0.65 * seen);
22
+ });
23
+ playhead.style.left = `${progress * 100}%`;
24
+ };
25
+ const dispose = () => {
26
+ inner.dispose();
27
+ for (const bar of bars) {
28
+ bar.style.removeProperty("height");
29
+ bar.style.removeProperty("opacity");
30
+ }
31
+ playhead.style.removeProperty("left");
32
+ };
33
+ seek(0);
34
+ return { seek, dispose, ready: inner.ready };
35
+ }
@@ -0,0 +1,44 @@
1
+ #scene-root {
2
+ align-items: center;
3
+ text-align: center;
4
+ }
5
+ #scene-root .mk-stage {
6
+ flex: 1;
7
+ width: 100%;
8
+ min-height: 0;
9
+ display: flex;
10
+ flex-direction: column;
11
+ justify-content: center;
12
+ align-items: stretch;
13
+ gap: var(--mk-space-md);
14
+ position: relative;
15
+ }
16
+ #scene-root .mk-wave {
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 6px;
20
+ height: 40vmin;
21
+ }
22
+ #scene-root .mk-bar {
23
+ flex: 1 1 0;
24
+ min-width: 2px;
25
+ border-radius: var(--mk-radius-full);
26
+ background: var(--mk-color-accent);
27
+ height: 4%;
28
+ }
29
+ #scene-root .mk-playhead {
30
+ position: absolute;
31
+ top: 8%;
32
+ bottom: 8%;
33
+ left: 0;
34
+ width: 3px;
35
+ border-radius: var(--mk-radius-full);
36
+ background: var(--mk-color-fg);
37
+ opacity: 0.85;
38
+ }
39
+ #scene-root .mk-label {
40
+ font-family: var(--mk-font-sans);
41
+ font-size: var(--mk-font-size-lg);
42
+ color: var(--mk-color-muted);
43
+ margin: 0;
44
+ }
@@ -0,0 +1,24 @@
1
+ export interface FitTextOptions {
2
+ /** Available line width in px. */
3
+ readonly boxWidth: number;
4
+ readonly maxFont: number;
5
+ readonly minFont: number;
6
+ readonly maxLines: number;
7
+ /** Last line must be at least this fraction of the widest line (default 0.35). */
8
+ readonly minLastLine?: number;
9
+ }
10
+ export interface FittedText {
11
+ /** Lines, split only at spaces (a word is never broken). */
12
+ readonly lines: readonly string[];
13
+ readonly fontPx: number;
14
+ /** Every line fits `boxWidth`, within `maxLines`, last line not a stub. */
15
+ readonly fits: boolean;
16
+ /** Words wider than the box on their own. */
17
+ readonly overlong: readonly string[];
18
+ }
19
+ /**
20
+ * Largest font in [minFont, maxFont] whose balanced wrap fits the box in at most `maxLines`
21
+ * lines, never breaking a word and never leaving a stub last line. When nothing fits, returns the
22
+ * single-line text at `minFont` with `fits: false` and the offending words.
23
+ */
24
+ export declare const fitText: (text: string, opts: FitTextOptions) => FittedText;