@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,155 @@
1
+ #scene-root {
2
+ align-items: center;
3
+ }
4
+ #scene-root .mk-window {
5
+ min-width: 56%;
6
+ max-width: 100%;
7
+ overflow: hidden;
8
+ background: var(--mk-color-card);
9
+ border: var(--mk-stroke-card, 2px) solid var(--mk-color-divider);
10
+ border-radius: var(--mk-radius-xl);
11
+ box-shadow: var(
12
+ --mk-shadow-card,
13
+ 0 32px 72px -32px color-mix(in srgb, var(--mk-color-fg) 45%, transparent)
14
+ );
15
+ }
16
+ #scene-root .mk-size-lg {
17
+ font-size: var(--mk-font-size-lg);
18
+ }
19
+ #scene-root .mk-size-md {
20
+ font-size: var(--mk-font-size-md);
21
+ }
22
+ #scene-root .mk-size-sm {
23
+ font-size: var(--mk-font-size-sm);
24
+ }
25
+ #scene-root .mk-bar {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: var(--mk-space-lg);
29
+ padding: var(--mk-space-md) var(--mk-space-lg);
30
+ min-height: 72px;
31
+ background: var(--mk-color-surface);
32
+ border-bottom: 2px solid var(--mk-color-divider);
33
+ }
34
+ #scene-root .mk-dots {
35
+ display: flex;
36
+ gap: var(--mk-space-md);
37
+ }
38
+ #scene-root .mk-dots i {
39
+ display: block;
40
+ width: 20px;
41
+ height: 20px;
42
+ border-radius: var(--mk-radius-full);
43
+ background: var(--mk-color-divider);
44
+ }
45
+ #scene-root .mk-file {
46
+ font-family: var(--mk-font-mono);
47
+ font-size: var(--mk-font-size-sm);
48
+ font-weight: 600;
49
+ color: var(--mk-color-muted);
50
+ }
51
+ #scene-root .mk-body {
52
+ display: flex;
53
+ flex-direction: column;
54
+ padding: var(--mk-space-lg) 0;
55
+ font-family: var(--mk-font-mono);
56
+ line-height: 1.6;
57
+ color: var(--mk-color-fg);
58
+ }
59
+ #scene-root .mk-line {
60
+ display: grid;
61
+ grid-template-columns: 2ch 2ch auto;
62
+ column-gap: 1ch;
63
+ align-items: baseline;
64
+ padding: 0 var(--mk-space-lg) 0 var(--mk-space-md);
65
+ white-space: pre;
66
+ min-height: 1.6em;
67
+ }
68
+ #scene-root .mk-bare .mk-line {
69
+ grid-template-columns: 0 2ch auto;
70
+ column-gap: 0;
71
+ }
72
+ #scene-root .mk-num {
73
+ text-align: right;
74
+ color: var(--mk-color-muted);
75
+ font-variant-numeric: var(--mk-numeric, tabular-nums);
76
+ opacity: 0.7;
77
+ }
78
+ #scene-root .mk-sign {
79
+ text-align: center;
80
+ font-weight: 800;
81
+ }
82
+ #scene-root .mk-code {
83
+ position: relative;
84
+ display: block;
85
+ }
86
+ #scene-root .mk-text {
87
+ display: inline-block;
88
+ min-width: 1px;
89
+ }
90
+ #scene-root .mk-caret {
91
+ position: absolute;
92
+ top: 0.15em;
93
+ bottom: 0.15em;
94
+ left: 0;
95
+ width: 0.5ch;
96
+ background: var(--mk-color-accent);
97
+ opacity: 0;
98
+ }
99
+ #scene-root .mk-add {
100
+ background: color-mix(in srgb, var(--mk-chart-3) 18%, var(--mk-color-card));
101
+ }
102
+ #scene-root .mk-add .mk-sign {
103
+ color: var(--mk-chart-3);
104
+ }
105
+ #scene-root .mk-del {
106
+ background: color-mix(in srgb, var(--mk-chart-5) 16%, var(--mk-color-card));
107
+ }
108
+ #scene-root .mk-del .mk-sign {
109
+ color: var(--mk-chart-5);
110
+ }
111
+ #scene-root .mk-hl {
112
+ background: var(--mk-color-accentSoft);
113
+ box-shadow: inset 8px 0 0 var(--mk-color-accent);
114
+ }
115
+ #scene-root .mk-t-kw {
116
+ color: var(--mk-color-accent);
117
+ font-weight: 700;
118
+ }
119
+ #scene-root .mk-t-str {
120
+ color: var(--mk-color-accent2);
121
+ }
122
+ #scene-root .mk-t-num {
123
+ color: var(--mk-chart-2);
124
+ }
125
+ #scene-root .mk-t-com {
126
+ color: var(--mk-color-muted);
127
+ font-style: italic;
128
+ }
129
+ #scene-root .mk-t-fn {
130
+ color: var(--mk-chart-6);
131
+ font-weight: 700;
132
+ }
133
+ #scene-root .mk-t-type {
134
+ color: var(--mk-chart-4);
135
+ }
136
+ #scene-root .mk-t-key {
137
+ color: var(--mk-color-accent);
138
+ }
139
+ #scene-root .mk-t-pun {
140
+ color: var(--mk-color-muted);
141
+ }
142
+ #scene-root .mk-wrap {
143
+ width: 100%;
144
+ }
145
+ #scene-root .mk-wrap .mk-line {
146
+ grid-template-columns: 2ch 2ch minmax(0, 1fr);
147
+ white-space: pre-wrap;
148
+ overflow-wrap: anywhere;
149
+ }
150
+ #scene-root .mk-wrap.mk-bare .mk-line {
151
+ grid-template-columns: 0 2ch minmax(0, 1fr);
152
+ }
153
+ #scene-root .mk-wrap .mk-text {
154
+ display: block;
155
+ }
@@ -0,0 +1,8 @@
1
+ export type Lang = "js" | "ts" | "json" | "py" | "sh" | "plain";
2
+ export type TokenKind = "plain" | "kw" | "str" | "num" | "com" | "fn" | "type" | "key" | "pun";
3
+ export interface Token {
4
+ readonly kind: TokenKind;
5
+ readonly text: string;
6
+ }
7
+ /** Splits one line into classified tokens; lossless, deterministic, no highlight library. */
8
+ export declare const tokenize: (line: string, lang: Lang) => Token[];
@@ -0,0 +1,145 @@
1
+ const words = (list) => new Set(list.split(" "));
2
+ const JS_KEYWORDS = words("const let var function return if else for while do switch case break continue new class extends import export from default async await try catch finally throw typeof instanceof in of void yield this null undefined true false");
3
+ const C_COMMENT = /\/\/.*|\/\*.*?(?:\*\/|$)/y;
4
+ const HASH_COMMENT = /#.*/y;
5
+ const RULES = {
6
+ js: {
7
+ keywords: JS_KEYWORDS,
8
+ comment: C_COMMENT,
9
+ quotes: "\"'`",
10
+ types: false,
11
+ declares: words("function class"),
12
+ },
13
+ ts: {
14
+ keywords: new Set([
15
+ ...JS_KEYWORDS,
16
+ ...words("interface type enum implements readonly public private protected as satisfies keyof"),
17
+ ]),
18
+ comment: C_COMMENT,
19
+ quotes: "\"'`",
20
+ types: true,
21
+ declares: words("function class"),
22
+ },
23
+ json: {
24
+ keywords: words("true false null"),
25
+ comment: undefined,
26
+ quotes: '"',
27
+ types: false,
28
+ declares: words(""),
29
+ },
30
+ py: {
31
+ keywords: words("def class return if elif else for while in not and or is import from as with try except finally raise lambda pass break continue yield async await None True False"),
32
+ comment: HASH_COMMENT,
33
+ quotes: "\"'",
34
+ types: false,
35
+ declares: words("def class"),
36
+ },
37
+ sh: {
38
+ keywords: words("if then else elif fi for while do done case esac in function export"),
39
+ comment: HASH_COMMENT,
40
+ quotes: "\"'",
41
+ types: false,
42
+ declares: words(""),
43
+ },
44
+ };
45
+ const NUMBER = /-?\d+(?:\.\d+)?(?:e[+-]?\d+)?/iy;
46
+ const IDENT = /[A-Za-z_$@][\w$]*/y;
47
+ const FLAG = /--?[A-Za-z][\w-]*/y;
48
+ const SH_VAR = /\$[A-Za-z_]\w*|\$\{[^}]*\}/y;
49
+ const PROMPT = /^\s*[$>] /;
50
+ const matchAt = (re, text, at) => {
51
+ re.lastIndex = at;
52
+ return re.exec(text)?.[0];
53
+ };
54
+ /** Index just past the closing quote, or the end of the line when unterminated. */
55
+ const stringEnd = (line, at) => {
56
+ const quote = line[at];
57
+ let i = at + 1;
58
+ while (i < line.length) {
59
+ if (line[i] === "\\")
60
+ i += 2;
61
+ else if (line[i] === quote)
62
+ return i + 1;
63
+ else
64
+ i += 1;
65
+ }
66
+ return line.length;
67
+ };
68
+ const nextChar = (line, from) => line.slice(from).trimStart()[0] ?? "";
69
+ const push = (out, kind, text) => {
70
+ const last = out[out.length - 1];
71
+ if (last && last.kind === kind)
72
+ out[out.length - 1] = { kind, text: last.text + text };
73
+ else
74
+ out.push({ kind, text });
75
+ };
76
+ const wordKind = (lang, rules, word, after, at) => {
77
+ if (rules.keywords.has(word))
78
+ return "kw";
79
+ if (lang === "sh")
80
+ return at.first ? "fn" : "plain";
81
+ if (lang === "py" && word.startsWith("@"))
82
+ return "fn";
83
+ if (rules.declares.has(at.prev) || nextChar(after, 0) === "(")
84
+ return "fn";
85
+ return rules.types && /^[A-Z]/.test(word) ? "type" : "plain";
86
+ };
87
+ const quoted = (line, i, lang) => {
88
+ const end = stringEnd(line, i);
89
+ const key = lang === "json" && nextChar(line, end) === ":";
90
+ return { kind: key ? "key" : "str", text: line.slice(i, end) };
91
+ };
92
+ /** Shell variables and flags (a flag must follow whitespace). */
93
+ const shellPiece = (line, i) => {
94
+ const variable = matchAt(SH_VAR, line, i);
95
+ if (variable !== undefined)
96
+ return variable;
97
+ return /\s/.test(line[i - 1] ?? " ") ? matchAt(FLAG, line, i) : undefined;
98
+ };
99
+ /** A number never starts inside a word (v2, a1). */
100
+ const numeric = (line, i) => /\w/.test(line[i - 1] ?? " ") ? undefined : matchAt(NUMBER, line, i);
101
+ const word = (line, i, lang, rules, at) => {
102
+ const ident = matchAt(IDENT, line, i);
103
+ if (ident === undefined)
104
+ return undefined;
105
+ const kind = wordKind(lang, rules, ident, line.slice(i + ident.length), at);
106
+ at.prev = ident;
107
+ at.first = false;
108
+ return { kind, text: ident };
109
+ };
110
+ /** One step: the token starting at `i`, or a single plain character. */
111
+ const step = (line, i, lang, rules, at) => {
112
+ const char = line[i] ?? "";
113
+ const comment = rules.comment && matchAt(rules.comment, line, i);
114
+ if (comment)
115
+ return { kind: "com", text: comment };
116
+ if (rules.quotes.includes(char))
117
+ return quoted(line, i, lang);
118
+ const shell = lang === "sh" ? shellPiece(line, i) : undefined;
119
+ if (shell)
120
+ return { kind: "type", text: shell };
121
+ const number = lang === "sh" ? undefined : numeric(line, i);
122
+ if (number)
123
+ return { kind: "num", text: number };
124
+ return word(line, i, lang, rules, at) ?? { kind: "plain", text: char };
125
+ };
126
+ /** Splits one line into classified tokens; lossless, deterministic, no highlight library. */
127
+ export const tokenize = (line, lang) => {
128
+ if (lang === "plain")
129
+ return [{ kind: "plain", text: line }];
130
+ const rules = RULES[lang];
131
+ const out = [];
132
+ const at = { prev: "", first: true };
133
+ let i = 0;
134
+ const prompt = lang === "sh" ? PROMPT.exec(line)?.[0] : undefined;
135
+ if (prompt !== undefined) {
136
+ push(out, "pun", prompt);
137
+ i = prompt.length;
138
+ }
139
+ while (i < line.length) {
140
+ const token = step(line, i, lang, rules, at);
141
+ push(out, token.kind, token.text);
142
+ i += token.text.length;
143
+ }
144
+ return out;
145
+ };
@@ -0,0 +1 @@
1
+ export declare const compareScene: import("@anvpx/motion-schema").ScenePlugin;
@@ -0,0 +1,128 @@
1
+ import { z } from "zod";
2
+ import { defineScene } from "../../define-scene.js";
3
+ import { animAttrs, esc } from "../../html.js";
4
+ import { frameCss, frameOf } from "../../shared/frame.js";
5
+ import { sceneIcon } from "../../shared/icon.js";
6
+ import { readText } from "../../text-file.js";
7
+ import { limitWords, present, text } from "../../words.js";
8
+ const COUNT_MESSAGE = "need 1-5 points";
9
+ const MARK_SIZE = 36;
10
+ const BADGE_SIZE = 32;
11
+ /** Above this many points a side uses the compact point size. */
12
+ const DENSE_POINTS = 3;
13
+ const capped = (max) => text.max(max, `max ${max} characters`);
14
+ const sideSchema = z.strictObject({
15
+ label: capped(30),
16
+ points: z.array(capped(40)).min(1, COUNT_MESSAGE).max(5, COUNT_MESSAGE),
17
+ value: capped(12).optional(),
18
+ tag: capped(20).optional(),
19
+ });
20
+ const configSchema = limitWords(z.strictObject({
21
+ title: capped(60).optional(),
22
+ left: sideSchema,
23
+ right: sideSchema,
24
+ mode: z.enum(["split", "wipe"]).default("split"),
25
+ verdict: capped(80).optional(),
26
+ winner: z.enum(["left", "right"]).optional(),
27
+ winnerLabel: capped(20).default("Winner"),
28
+ }), (props) => [
29
+ ...present(props.title, props.verdict, props.winner && props.winnerLabel),
30
+ ...[props.left, props.right].flatMap((s) => present(s.tag, s.label, s.value, ...s.points)),
31
+ ]);
32
+ const badge = (label) => `<span class="mk-badge">${sceneIcon("trophy", { at: "props.winner", size: BADGE_SIZE })}<span>${esc(label)}</span></span>`;
33
+ const point = (props, pos, label) => {
34
+ const lost = props.winner !== undefined && props.winner !== pos;
35
+ const icon = sceneIcon(lost ? "minus" : "check", { at: `props.${pos}.points`, size: MARK_SIZE });
36
+ return `<li class="mk-point"><span class="mk-mark">${icon}</span><span>${esc(label)}</span></li>`;
37
+ };
38
+ const card = (props, pos, attrs, wrap = false, badged = true) => {
39
+ const side = props[pos];
40
+ const cls = ["mk-side", `mk-${pos}`];
41
+ if (props.winner === pos)
42
+ cls.push("mk-win");
43
+ if (props.winner !== undefined && props.winner !== pos)
44
+ cls.push("mk-lose");
45
+ const tag = side.tag ? `<span class="mk-tag">${esc(side.tag)}</span>` : "";
46
+ const value = side.value ? `<p class="mk-value">${esc(side.value)}</p>` : "";
47
+ const win = badged && props.winner === pos ? badge(props.winnerLabel) : "";
48
+ const head = `<header class="mk-side-head">${tag}${win}</header>
49
+ <h2 class="mk-label">${esc(side.label)}</h2>
50
+ ${value}`;
51
+ return `<article class="${cls.join(" ")}"${attrs ? ` ${attrs}` : ""}>
52
+ ${wrap ? `<div class="mk-col">${head}</div>` : head}
53
+ <ul class="mk-points">
54
+ ${side.points.map((p) => point(props, pos, p)).join("\n")}
55
+ </ul>
56
+ </article>`;
57
+ };
58
+ /** Wipe mode hides the left card at the end, so the winner is flagged by name below both layers. */
59
+ const flag = (props) => props.winner === undefined
60
+ ? ""
61
+ : `
62
+ <span class="mk-flag" data-late>${badge(`${props.winnerLabel}: ${props[props.winner].label}`)}</span>`;
63
+ const verdict = (props, wipe, step) => {
64
+ if (!props.verdict)
65
+ return "";
66
+ const attrs = wipe ? "data-late" : animAttrs("rise", step, "verdict");
67
+ return `<p class="mk-verdict" ${attrs}>${esc(props.verdict)}</p>`;
68
+ };
69
+ /** Tall frames: panels stack in one column; the wipe sweeps top to bottom. */
70
+ const TALL_CSS = `#scene-root .mk-split { grid-template-columns: minmax(0, 1fr); }
71
+ #scene-root .mk-over { clip-path: inset(0 0 100% 0); }
72
+ #scene-root .mk-bar { top: 0; bottom: auto; left: calc(-1 * var(--mk-space-md)); right: calc(-1 * var(--mk-space-md)); width: auto; height: 6px; margin: -3px 0 0; }
73
+ #scene-root .mk-side { padding: var(--mk-space-lg); }`;
74
+ /** Tall wipe cards stack heading over points (two columns are too narrow at 1080 px). */
75
+ const WIPE_STACK_CSS = `#scene-root .mk-wipe .mk-side { grid-template-columns: minmax(0, 1fr); row-gap: var(--mk-space-md); align-items: start; }
76
+ #scene-root .mk-wipe .mk-points { padding: var(--mk-space-md) 0 0; border-left: 0; border-top: 2px solid var(--mk-color-divider); }`;
77
+ /** Square: each stacked split panel puts its points beside the heading to stay short. */
78
+ const SQUARE_CSS = `#scene-root .mk-split .mk-side { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); column-gap: var(--mk-space-lg); row-gap: var(--mk-space-sm); align-content: center; padding: var(--mk-space-md) var(--mk-space-lg); }
79
+ #scene-root .mk-split .mk-side > :not(.mk-points) { grid-column: 1; }
80
+ #scene-root .mk-split .mk-points { grid-column: 2; grid-row: 1 / span 3; margin: 0; padding-left: var(--mk-space-lg); border-left: 2px solid var(--mk-color-divider); justify-content: center; }
81
+ #scene-root .mk-split .mk-label { overflow-wrap: normal; }
82
+ #scene-root .mk-compare .mk-split .mk-point, #scene-root .mk-compare .mk-wipe .mk-point { font-size: var(--mk-font-size-md); }
83
+ #scene-root .mk-wipe .mk-side { row-gap: var(--mk-space-sm); padding: var(--mk-space-md) var(--mk-space-lg); }
84
+ #scene-root .mk-wipe .mk-points { gap: var(--mk-space-sm); }
85
+ #scene-root .mk-split .mk-value { font-size: var(--mk-font-size-xl); }`;
86
+ const render = (props, tall) => {
87
+ const wipe = props.mode === "wipe";
88
+ const dense = [props.left, props.right].some((s) => s.points.length > DENSE_POINTS);
89
+ const head = props.title
90
+ ? `<div class="mk-head"><h1 class="mk-heading" ${animAttrs("rise", 0, "title")}>${esc(props.title)}</h1></div>\n`
91
+ : "";
92
+ const stage = wipe
93
+ ? `<div class="mk-stage mk-wipe">
94
+ <div class="mk-layer" ${animAttrs("rise", 1, "left")}>${card(props, "left", "", true, false)}</div>
95
+ <div class="mk-layer mk-over" data-wipe${tall ? ' data-axis="y"' : ""}>${card(props, "right", "", true, false)}</div>
96
+ <span class="mk-bar" data-bar aria-hidden="true"></span>
97
+ </div>${flag(props)}`
98
+ : `<div class="mk-stage mk-split">
99
+ ${card(props, "left", animAttrs("rise", 1, "left"))}
100
+ ${card(props, "right", animAttrs("rise", 2, "right"))}
101
+ </div>`;
102
+ return `<div class="mk-compare${dense ? " mk-dense" : ""}">
103
+ ${head}${stage}
104
+ ${verdict(props, wipe, 4)}
105
+ </div>`;
106
+ };
107
+ export const compareScene = defineScene({
108
+ type: "compare",
109
+ configSchema,
110
+ meta: {
111
+ purpose: "Two options side by side or wiped before/after, 1-5 points each; the winner is marked by icon and text.",
112
+ example: {
113
+ left: { label: "Before", points: ["Manual"] },
114
+ right: { label: "After", points: ["Automated"] },
115
+ winner: "right",
116
+ },
117
+ },
118
+ style: readText("./style.css", import.meta.url),
119
+ script: readText("./scene.js", import.meta.url),
120
+ render: (props, ctx, root) => ({
121
+ body: render(props, frameOf(ctx) !== "landscape"),
122
+ css: frameCss(ctx, root, {
123
+ tall: `${TALL_CSS}
124
+ ${WIPE_STACK_CSS}`,
125
+ square: SQUARE_CSS,
126
+ }),
127
+ }),
128
+ });
@@ -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,58 @@
1
+ // @ts-check
2
+ import { ease } from "../../runtime/easing.js";
3
+ import { mountTimeline } from "../../runtime/mount.js";
4
+ import { progress, wipe } from "../../shared/motion.js";
5
+ const DEFAULT_DURATION = 6;
6
+ /** Wipe window as shares of the scene, with an upper bound on its length in seconds. */
7
+ const WIPE_START = 0.38;
8
+ const WIPE_SHARE = 0.28;
9
+ const WIPE_MAX = 1.4;
10
+ const LATE_FADE = 0.5;
11
+ /** Top-to-bottom wipe: the share of the layer still hidden is clipped from the bottom.
12
+ * @param {number} p */
13
+ const wipeDown = (p) => `inset(0 0 ${Math.round((1 - progress(p, 0, 1)) * 100000) / 1000}% 0)`;
14
+ /**
15
+ * @param {HTMLElement} bar
16
+ * @param {"top" | "left"} edge
17
+ * @param {number} p
18
+ */
19
+ const placeBar = (bar, edge, p) => {
20
+ bar.style[edge] = `${p * 100}%`;
21
+ bar.style.opacity = String(p > 0 && p < 1 ? 1 : 0);
22
+ };
23
+ /** @type {import("../../runtime/mount.js").CreateScene} */
24
+ export function createScene(root, config, ctx) {
25
+ const inner = mountTimeline(root, config, ctx, { baseDelay: 0.2, staggerFrames: 5, rise: 36 });
26
+ const duration = ctx.duration || Number(root.dataset.duration) || DEFAULT_DURATION;
27
+ const start = duration * WIPE_START;
28
+ const end = start + Math.min(WIPE_MAX, duration * WIPE_SHARE);
29
+ const layer = /** @type {HTMLElement | null} */ (root.querySelector("[data-wipe]"));
30
+ const bar = /** @type {HTMLElement | null} */ (root.querySelector("[data-bar]"));
31
+ const vertical = layer?.dataset.axis === "y";
32
+ const edge = vertical ? "top" : "left";
33
+ const clipOf = vertical ? wipeDown : wipe;
34
+ const late = /** @type {HTMLElement[]} */ (Array.from(root.querySelectorAll("[data-late]")));
35
+ /** @param {number} t */
36
+ const seek = (t) => {
37
+ inner.seek(t);
38
+ const p = ease("in-out", progress(t, start, end));
39
+ if (layer)
40
+ layer.style.clipPath = clipOf(p);
41
+ if (bar)
42
+ placeBar(bar, edge, p);
43
+ const shown = ease("out", progress(t, end, end + LATE_FADE));
44
+ for (const el of late)
45
+ el.style.opacity = String(shown);
46
+ };
47
+ const dispose = () => {
48
+ inner.dispose();
49
+ layer?.style.removeProperty("clip-path");
50
+ for (const el of [bar, ...late]) {
51
+ el?.style.removeProperty("opacity");
52
+ el?.style.removeProperty("left");
53
+ el?.style.removeProperty("top");
54
+ }
55
+ };
56
+ seek(0);
57
+ return { seek, dispose, ready: inner.ready };
58
+ }
@@ -0,0 +1,187 @@
1
+ #scene-root .mk-compare {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--mk-space-lg);
5
+ min-height: 0;
6
+ }
7
+ #scene-root .mk-head {
8
+ display: flex;
9
+ flex-direction: column;
10
+ align-items: flex-start;
11
+ }
12
+ #scene-root .mk-heading {
13
+ font-family: var(--mk-font-display, var(--mk-font-sans));
14
+ font-size: var(--mk-font-size-xl);
15
+ font-weight: 800;
16
+ line-height: 1.04;
17
+ letter-spacing: -0.03em;
18
+ text-wrap: balance;
19
+ }
20
+ #scene-root .mk-stage {
21
+ position: relative;
22
+ display: grid;
23
+ gap: var(--mk-space-lg);
24
+ }
25
+ #scene-root .mk-split {
26
+ grid-template-columns: 1fr 1fr;
27
+ }
28
+ #scene-root .mk-wipe {
29
+ grid-template-columns: 1fr;
30
+ grid-template-rows: 1fr;
31
+ }
32
+ #scene-root .mk-layer {
33
+ grid-area: 1 / 1;
34
+ display: grid;
35
+ }
36
+ #scene-root .mk-over {
37
+ clip-path: inset(0 100% 0 0);
38
+ }
39
+ #scene-root .mk-bar {
40
+ position: absolute;
41
+ top: calc(-1 * var(--mk-space-md));
42
+ bottom: calc(-1 * var(--mk-space-md));
43
+ left: 0;
44
+ width: 6px;
45
+ margin-left: -3px;
46
+ border-radius: var(--mk-radius-full);
47
+ background: var(--mk-color-accent);
48
+ opacity: 0;
49
+ }
50
+ #scene-root .mk-side {
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: var(--mk-space-md);
54
+ padding: var(--mk-space-lg) var(--mk-space-xl) var(--mk-space-xl);
55
+ background: var(--mk-color-card);
56
+ border: var(--mk-stroke-card, 2px) solid var(--mk-color-divider);
57
+ border-radius: var(--mk-radius-xl);
58
+ box-shadow: var(
59
+ --mk-shadow-card,
60
+ 0 24px 56px -28px color-mix(in srgb, var(--mk-color-fg) 40%, transparent)
61
+ );
62
+ }
63
+ #scene-root .mk-col {
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: var(--mk-space-md);
67
+ }
68
+ #scene-root .mk-side-head {
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: space-between;
72
+ gap: var(--mk-space-md);
73
+ min-height: 56px;
74
+ }
75
+ #scene-root .mk-tag {
76
+ padding: var(--mk-space-xs) var(--mk-space-md);
77
+ border-radius: var(--mk-radius-full);
78
+ background: var(--mk-color-accentSoft);
79
+ color: var(--mk-color-fg);
80
+ font-size: var(--mk-font-size-sm);
81
+ font-weight: 700;
82
+ }
83
+ #scene-root .mk-badge {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ gap: var(--mk-space-sm);
87
+ margin-left: auto;
88
+ padding: var(--mk-space-xs) var(--mk-space-md);
89
+ border-radius: var(--mk-radius-full);
90
+ background: var(--mk-color-accent);
91
+ color: var(--mk-color-onAccent);
92
+ font-size: var(--mk-font-size-sm);
93
+ font-weight: 800;
94
+ }
95
+ #scene-root .mk-flag {
96
+ align-self: center;
97
+ opacity: 0;
98
+ }
99
+ #scene-root .mk-label {
100
+ font-size: var(--mk-font-size-xl);
101
+ font-weight: 800;
102
+ line-height: 1.04;
103
+ letter-spacing: -0.02em;
104
+ text-wrap: balance;
105
+ }
106
+ #scene-root .mk-value {
107
+ font-family: var(--mk-font-display, var(--mk-font-sans));
108
+ font-size: var(--mk-font-size-title);
109
+ font-weight: 800;
110
+ line-height: 1;
111
+ letter-spacing: -0.03em;
112
+ font-variant-numeric: var(--mk-numeric, tabular-nums);
113
+ color: var(--mk-color-accent);
114
+ }
115
+ #scene-root .mk-points {
116
+ list-style: none;
117
+ padding: 0;
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: var(--mk-space-md);
121
+ margin-top: var(--mk-space-sm);
122
+ }
123
+ #scene-root .mk-point {
124
+ display: grid;
125
+ grid-template-columns: 36px 1fr;
126
+ column-gap: var(--mk-space-md);
127
+ align-items: start;
128
+ font-size: var(--mk-font-size-lg);
129
+ font-weight: 600;
130
+ line-height: 1.2;
131
+ }
132
+ #scene-root .mk-mark {
133
+ display: grid;
134
+ place-items: center;
135
+ height: 1.2em;
136
+ color: var(--mk-color-accent);
137
+ }
138
+ #scene-root .mk-verdict {
139
+ align-self: center;
140
+ padding: var(--mk-space-md) var(--mk-space-xl);
141
+ border-radius: var(--mk-radius-full);
142
+ background: var(--mk-color-accent);
143
+ color: var(--mk-color-onAccent);
144
+ font-size: var(--mk-font-size-lg);
145
+ font-weight: 700;
146
+ text-align: center;
147
+ }
148
+ #scene-root .mk-win {
149
+ border: 4px solid var(--mk-color-accent);
150
+ background: color-mix(in srgb, var(--mk-color-accentSoft) 55%, var(--mk-color-card));
151
+ }
152
+ #scene-root .mk-lose .mk-mark {
153
+ color: var(--mk-color-muted);
154
+ }
155
+ #scene-root .mk-lose .mk-label,
156
+ #scene-root .mk-lose .mk-value {
157
+ color: var(--mk-color-muted);
158
+ }
159
+ #scene-root .mk-dense .mk-point {
160
+ font-size: var(--mk-font-size-md);
161
+ }
162
+ #scene-root .mk-wipe .mk-side {
163
+ display: grid;
164
+ grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
165
+ column-gap: var(--mk-space-xl);
166
+ align-items: center;
167
+ box-shadow: none;
168
+ }
169
+ #scene-root .mk-wipe .mk-points {
170
+ margin: 0;
171
+ gap: var(--mk-space-lg);
172
+ padding-left: var(--mk-space-xl);
173
+ border-left: 2px solid var(--mk-color-divider);
174
+ }
175
+ #scene-root .mk-wipe .mk-left {
176
+ background: var(--mk-color-surface);
177
+ }
178
+ #scene-root .mk-wipe .mk-right {
179
+ border-color: var(--mk-color-accent);
180
+ }
181
+ #scene-root .mk-wipe .mk-left .mk-label,
182
+ #scene-root .mk-wipe .mk-left .mk-value {
183
+ color: var(--mk-color-muted);
184
+ }
185
+ #scene-root .mk-compare .mk-wipe .mk-point {
186
+ font-size: var(--mk-font-size-lg);
187
+ }
@@ -0,0 +1 @@
1
+ export declare const countdownScene: import("@anvpx/motion-schema").ScenePlugin;