@phin-tech/dottie 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PARITY.md CHANGED
@@ -38,6 +38,11 @@ fixture, so the gap is visible rather than silent.
38
38
  | Damaged timelines (clamped, filtered, sorted) | Supported | [edge/timeline](../fixtures/edge/timeline.dottie) | Read the way DottieKit's decoders read them |
39
39
  | Unknown kinds, fields and newer versions | Partial | [js-only/future](../fixtures/js-only/future.dottie) | Drawn as labelled placeholders, reported in `warnings`; Dottie refuses such files |
40
40
  | Text fonts | Partial | | Positions use Helvetica Neue's metrics; browsers without Helvetica Neue draw Helvetica or Arial in the same places, so runs of mixed styles may sit slightly apart |
41
- | Links between shapes and pages | Missing | | Not clickable in the player yet |
42
- | Interactivity (hover, clicks, editing) | Missing | | |
41
+ | Symbols: instances on their own clocks (loop, once, still, offset), nested, scaled and turned | Supported | [edge/symbols](../fixtures/edge/symbols.dottie) | Expanded as DottieKit expands them, with the same ids, seeds and limits ([js-only/hostile-symbols](../fixtures/js-only/hostile-symbols.dottie)) |
42
+ | Button symbols (up, over, down, hit) and clickable copies | Supported | [edge/buttons](../fixtures/edge/buttons.dottie) | As the HTML export draws them |
43
+ | Symbol parameters (`{{name}}` in a symbol's text, filled per copy) | Supported | [edge/params](../fixtures/edge/params.dottie) | Defaults, undeclared names left as written, values passed into nested copies, filled before text types out |
44
+ | Copies that play when clicked | Supported | [edge/buttons](../fixtures/edge/buttons.dottie) | `<dottie-player>` only: the HTML export is drawn ahead of time and shows them at rest |
45
+ | Clicks: play a scene, open a link, hover looks, keyboard | Supported | [edge/clicks](../fixtures/edge/clicks.dottie) | As the HTML export does; only http, https and mailto links are followed |
46
+ | Links between shapes and pages | Partial | [edge/clicks](../fixtures/edge/clicks.dottie) | A link to a shape on another page shows that page; one on the same page does nothing. The HTML export holds one page, so it has neither |
47
+ | Editing | Missing | | |
43
48
  | Ticking task boxes in notes | Missing | | Drawn, not clickable |
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dottie.js
2
2
 
3
- `npm install @phin-tech/dottie` · [Live demos](https://phin-tech.github.io/dottie/dottie-js/)
3
+ `npm install @phin-tech/dottie` · [Live demos](https://dottie.phin.tech/dottie-js/)
4
4
 
5
5
  Render and play [Dottie](https://github.com/phin-tech/dottie) drawings in the browser, straight from the
6
6
  `.dottie` file: stills or animations, sketchy or clean, with no pre-rendered frames. It draws what Dottie draws:
@@ -29,8 +29,15 @@ prefer reduced motion. A still page is just the drawing.
29
29
  | `no-controls`, `no-autoplay`, `no-loop` | |
30
30
  | `label` | The accessible name; the page name by default |
31
31
 
32
- The element has `play()`, `pause()`, `nextScene()`, `prevScene()`, `currentTime` (settable), `duration`,
33
- `paused`, and fires `load`, `frame` and `error` events. Its page color comes from the drawing's theme; set
32
+ Elements the drawing makes clickable work with the mouse and the keyboard (Tab, then Enter or Space). A click plays
33
+ a named scene on to the next one, opens a link in a new tab (only `http`, `https` and `mailto`), or shows the page a
34
+ shape's link points at. Each shows its hover look under the pointer; button symbols show their over and down looks
35
+ and take clicks on their hit area, and a symbol's copy set to play when clicked plays its animation through. Every
36
+ click first fires a cancelable `action` event whose `detail` is `{element, scene | url | page, replay?}`; call
37
+ `preventDefault()` to handle it yourself.
38
+
39
+ The element has `play()`, `pause()`, `nextScene()`, `prevScene()`, `activate(elementId)`, `currentTime`
40
+ (settable), `duration`, `paused`, and fires `load`, `frame`, `action` and `error` events. Its page color comes from the drawing's theme; set
34
41
  `--dottie-radius` to round it, and style its parts with `::part(stage)` and `::part(controls)`.
35
42
 
36
43
  Without modules, `dist/dottie-player.min.js` is a classic script that registers the element and sets
package/dist/compile.js CHANGED
@@ -1,15 +1,16 @@
1
- /*! dottie.js 0.5.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
1
+ /*! dottie.js 0.5.2 | Apache-2.0 | https://github.com/phin-tech/dottie */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
 
6
6
  // src/model.ts
7
7
  var SUPPORTED_VERSION = 2;
8
- var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image"];
8
+ var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
9
9
  var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
10
10
  var KNOWN_FIELDS = {
11
11
  file: ["version", "pages", "images"],
12
- page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline"],
12
+ page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
13
+ symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
13
14
  layer: ["id", "name", "visible", "locked"],
14
15
  rgba: ["r", "g", "b", "a"],
15
16
  style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "gridSize", "opacity", "dashOffset"],
@@ -33,6 +34,12 @@ var KNOWN_FIELDS = {
33
34
  "rotation",
34
35
  "link",
35
36
  "anchor",
37
+ "click",
38
+ "hover",
39
+ "symbol",
40
+ "playback",
41
+ "offset",
42
+ "args",
36
43
  "image",
37
44
  "reveal"
38
45
  ],
@@ -47,7 +54,9 @@ var KNOWN_FIELDS = {
47
54
  follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
48
55
  repeat: ["start", "end", "count"],
49
56
  cameraKey: ["t", "x", "y", "width", "height", "ease"],
50
- marker: ["t", "name"]
57
+ marker: ["t", "name"],
58
+ click: ["scene", "url"],
59
+ hover: ["stroke", "fill", "opacity"]
51
60
  };
52
61
  var MAX_DURATION = 3600;
53
62
  var MAX_MARCH = 1e4;
@@ -59,6 +68,7 @@ var DottieFormatError = class extends Error {
59
68
  var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
60
69
  var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
61
70
  var str = (v) => typeof v === "string" ? v : void 0;
71
+ var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
62
72
  var bool = (v) => typeof v === "boolean" ? v : void 0;
63
73
  var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
64
74
  function clampTime(t) {
@@ -68,6 +78,11 @@ function clampTime(t) {
68
78
  function clampFPS(f) {
69
79
  return Number.isFinite(f) ? Math.min(Math.max(f, 1), 60) : 12;
70
80
  }
81
+ function allowedURL(url) {
82
+ const u = url.trim();
83
+ if (!/^(https?:\/\/|mailto:)/i.test(u) || [...u].length <= 7 || /[\u0000-\u001f\u007f-\u009f]/.test(u)) return null;
84
+ return u;
85
+ }
71
86
  function rgba(v) {
72
87
  if (!isObj(v)) return void 0;
73
88
  const r = num(v.r), g = num(v.g), b = num(v.b);
@@ -182,10 +197,63 @@ var Reader = class {
182
197
  rotation: num(v.rotation) ?? null,
183
198
  link: uuid(v.link) ?? null,
184
199
  anchor: str(v.anchor) ?? null,
200
+ click: this.click(v.click),
201
+ hover: this.hover(v.hover),
202
+ symbol: uuid(v.symbol) ?? null,
203
+ playback: this.playback(v.playback),
204
+ offset: num(v.offset) ?? null,
205
+ args: strings(v.args),
185
206
  image: this.image(v.image, images),
186
207
  reveal: num(v.reveal) ?? null
187
208
  };
188
209
  }
210
+ click(v) {
211
+ if (!isObj(v)) return null;
212
+ this.fields(v, "click");
213
+ const url = str(v.url);
214
+ const safe = url === void 0 ? null : allowedURL(url);
215
+ if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
216
+ return { scene: str(v.scene) ?? null, url: safe };
217
+ }
218
+ playback(v) {
219
+ const s = str(v);
220
+ if (s === void 0) return null;
221
+ return ["loop", "once", "still", "click"].includes(s) ? s : "loop";
222
+ }
223
+ symbol(v, images) {
224
+ if (!isObj(v)) return null;
225
+ this.fields(v, "symbol");
226
+ const id = uuid(v.id);
227
+ if (!id) {
228
+ this.warn("Skipped a symbol without an id");
229
+ return null;
230
+ }
231
+ const size = (x) => {
232
+ const n = num(x);
233
+ return Math.max(1, n ?? 100);
234
+ };
235
+ const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
236
+ const lid = uuid(l.id);
237
+ return lid ? [{ id: lid, name: str(l.name) ?? "", visible: bool(l.visible) ?? true, locked: bool(l.locked) ?? false }] : [];
238
+ });
239
+ if (layers.length === 0) layers.push({ id: `${id}-LAYER`, name: "Layer 1", visible: true, locked: false });
240
+ return {
241
+ id,
242
+ name: str(v.name) ?? "Symbol",
243
+ width: size(v.width),
244
+ height: size(v.height),
245
+ layers,
246
+ elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
247
+ timeline: this.timeline(v.timeline),
248
+ button: bool(v.button) ?? null,
249
+ params: strings(v.params)
250
+ };
251
+ }
252
+ hover(v) {
253
+ if (!isObj(v)) return null;
254
+ this.fields(v, "hover");
255
+ return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num(v.opacity) ?? null };
256
+ }
189
257
  ease(v) {
190
258
  const s = str(v);
191
259
  if (s === void 0) return null;
@@ -278,7 +346,8 @@ var Reader = class {
278
346
  layers,
279
347
  elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
280
348
  theme: str(v.theme) ?? null,
281
- timeline: this.timeline(v.timeline)
349
+ timeline: this.timeline(v.timeline),
350
+ symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
282
351
  };
283
352
  }
284
353
  };
@@ -318,6 +387,19 @@ function parseDottie(source) {
318
387
  return { version, pages, warnings: reader.warnings };
319
388
  }
320
389
 
390
+ // src/clicks.ts
391
+ var CLICK_STYLE = `
392
+ .dottie-a { cursor: pointer; outline: none; }
393
+ .dottie-a .dottie-h { display: none; }
394
+ .dottie-a:hover .dottie-h, .dottie-a.dottie-hover .dottie-h, .dottie-a:focus-visible .dottie-h { display: inline; }
395
+ .dottie-a:hover .dottie-n, .dottie-a.dottie-hover .dottie-n, .dottie-a:focus-visible .dottie-n { display: none; }
396
+ .dottie-a:focus-visible { filter: drop-shadow(0 0 3px var(--dottie-focus, #3b82f6)); }
397
+ .dottie-a:not(.dottie-b):active, .dottie-a.dottie-press:not(.dottie-b) { transform: translate(1px, 1px); }
398
+ .dottie-a .dottie-d { display: none; }
399
+ .dottie-b:active .dottie-n, .dottie-b:active .dottie-h, .dottie-b.dottie-press .dottie-n, .dottie-b.dottie-press .dottie-h { display: none; }
400
+ .dottie-b:active .dottie-d, .dottie-b.dottie-press .dottie-d { display: inline; }
401
+ `;
402
+
321
403
  // src/player.ts
322
404
  function pageIndex(file, spec) {
323
405
  const s = (spec ?? "").trim();
@@ -327,6 +409,22 @@ function pageIndex(file, spec) {
327
409
  const n = Number(s);
328
410
  return Number.isInteger(n) && n >= 1 && n <= file.pages.length ? n - 1 : 0;
329
411
  }
412
+ var STYLE = `
413
+ :host { display: block; position: relative; color: var(--dottie-ink); background: var(--dottie-page);
414
+ font: 14px -apple-system, system-ui, sans-serif; border-radius: var(--dottie-radius, 0); }
415
+ :host([hidden]) { display: none; }
416
+ .stage { display: block; width: 100%; height: auto; }
417
+ .stage svg { display: block; width: 100%; height: auto; margin: 0 auto; }
418
+ .bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px 12px; max-width: 560px; margin: 0 auto; box-sizing: border-box; }
419
+ .bar[hidden] { display: none; }
420
+ button { font: inherit; color: inherit; background: none; border: 1.5px solid currentColor; border-radius: 6px;
421
+ width: 34px; height: 30px; cursor: pointer; flex: none; padding: 0; }
422
+ button:focus-visible, input:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
423
+ input[type=range] { flex: 1; min-width: 60px; accent-color: currentColor; }
424
+ output { font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: right; }
425
+ .scene { min-width: 0; flex: 0 1 auto; text-align: center; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
426
+ .error { padding: 16px; font-family: ui-monospace, Menlo, monospace; white-space: pre-wrap; }
427
+ ${CLICK_STYLE}`;
330
428
 
331
429
  // src/compile.ts
332
430
  var LOADER = `;(function () {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- /*! dottie.js 0.5.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
2
+ /*! dottie.js 0.5.2 | Apache-2.0 | https://github.com/phin-tech/dottie */
3
3
  var __defProp = Object.defineProperty;
4
4
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
5
5
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -10,11 +10,12 @@ import { basename } from "node:path";
10
10
 
11
11
  // src/model.ts
12
12
  var SUPPORTED_VERSION = 2;
13
- var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image"];
13
+ var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
14
14
  var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
15
15
  var KNOWN_FIELDS = {
16
16
  file: ["version", "pages", "images"],
17
- page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline"],
17
+ page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
18
+ symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
18
19
  layer: ["id", "name", "visible", "locked"],
19
20
  rgba: ["r", "g", "b", "a"],
20
21
  style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "gridSize", "opacity", "dashOffset"],
@@ -38,6 +39,12 @@ var KNOWN_FIELDS = {
38
39
  "rotation",
39
40
  "link",
40
41
  "anchor",
42
+ "click",
43
+ "hover",
44
+ "symbol",
45
+ "playback",
46
+ "offset",
47
+ "args",
41
48
  "image",
42
49
  "reveal"
43
50
  ],
@@ -52,7 +59,9 @@ var KNOWN_FIELDS = {
52
59
  follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
53
60
  repeat: ["start", "end", "count"],
54
61
  cameraKey: ["t", "x", "y", "width", "height", "ease"],
55
- marker: ["t", "name"]
62
+ marker: ["t", "name"],
63
+ click: ["scene", "url"],
64
+ hover: ["stroke", "fill", "opacity"]
56
65
  };
57
66
  var MAX_DURATION = 3600;
58
67
  var MAX_MARCH = 1e4;
@@ -64,6 +73,7 @@ var DottieFormatError = class extends Error {
64
73
  var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
65
74
  var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
66
75
  var str = (v) => typeof v === "string" ? v : void 0;
76
+ var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
67
77
  var bool = (v) => typeof v === "boolean" ? v : void 0;
68
78
  var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
69
79
  function clampTime(t) {
@@ -73,6 +83,11 @@ function clampTime(t) {
73
83
  function clampFPS(f) {
74
84
  return Number.isFinite(f) ? Math.min(Math.max(f, 1), 60) : 12;
75
85
  }
86
+ function allowedURL(url) {
87
+ const u = url.trim();
88
+ if (!/^(https?:\/\/|mailto:)/i.test(u) || [...u].length <= 7 || /[\u0000-\u001f\u007f-\u009f]/.test(u)) return null;
89
+ return u;
90
+ }
76
91
  function rgba(v) {
77
92
  if (!isObj(v)) return void 0;
78
93
  const r = num(v.r), g = num(v.g), b = num(v.b);
@@ -187,10 +202,63 @@ var Reader = class {
187
202
  rotation: num(v.rotation) ?? null,
188
203
  link: uuid(v.link) ?? null,
189
204
  anchor: str(v.anchor) ?? null,
205
+ click: this.click(v.click),
206
+ hover: this.hover(v.hover),
207
+ symbol: uuid(v.symbol) ?? null,
208
+ playback: this.playback(v.playback),
209
+ offset: num(v.offset) ?? null,
210
+ args: strings(v.args),
190
211
  image: this.image(v.image, images),
191
212
  reveal: num(v.reveal) ?? null
192
213
  };
193
214
  }
215
+ click(v) {
216
+ if (!isObj(v)) return null;
217
+ this.fields(v, "click");
218
+ const url = str(v.url);
219
+ const safe = url === void 0 ? null : allowedURL(url);
220
+ if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
221
+ return { scene: str(v.scene) ?? null, url: safe };
222
+ }
223
+ playback(v) {
224
+ const s = str(v);
225
+ if (s === void 0) return null;
226
+ return ["loop", "once", "still", "click"].includes(s) ? s : "loop";
227
+ }
228
+ symbol(v, images) {
229
+ if (!isObj(v)) return null;
230
+ this.fields(v, "symbol");
231
+ const id = uuid(v.id);
232
+ if (!id) {
233
+ this.warn("Skipped a symbol without an id");
234
+ return null;
235
+ }
236
+ const size = (x) => {
237
+ const n = num(x);
238
+ return Math.max(1, n ?? 100);
239
+ };
240
+ const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
241
+ const lid = uuid(l.id);
242
+ return lid ? [{ id: lid, name: str(l.name) ?? "", visible: bool(l.visible) ?? true, locked: bool(l.locked) ?? false }] : [];
243
+ });
244
+ if (layers.length === 0) layers.push({ id: `${id}-LAYER`, name: "Layer 1", visible: true, locked: false });
245
+ return {
246
+ id,
247
+ name: str(v.name) ?? "Symbol",
248
+ width: size(v.width),
249
+ height: size(v.height),
250
+ layers,
251
+ elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
252
+ timeline: this.timeline(v.timeline),
253
+ button: bool(v.button) ?? null,
254
+ params: strings(v.params)
255
+ };
256
+ }
257
+ hover(v) {
258
+ if (!isObj(v)) return null;
259
+ this.fields(v, "hover");
260
+ return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num(v.opacity) ?? null };
261
+ }
194
262
  ease(v) {
195
263
  const s = str(v);
196
264
  if (s === void 0) return null;
@@ -283,7 +351,8 @@ var Reader = class {
283
351
  layers,
284
352
  elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
285
353
  theme: str(v.theme) ?? null,
286
- timeline: this.timeline(v.timeline)
354
+ timeline: this.timeline(v.timeline),
355
+ symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
287
356
  };
288
357
  }
289
358
  };
@@ -323,6 +392,19 @@ function parseDottie(source2) {
323
392
  return { version, pages, warnings: reader.warnings };
324
393
  }
325
394
 
395
+ // src/clicks.ts
396
+ var CLICK_STYLE = `
397
+ .dottie-a { cursor: pointer; outline: none; }
398
+ .dottie-a .dottie-h { display: none; }
399
+ .dottie-a:hover .dottie-h, .dottie-a.dottie-hover .dottie-h, .dottie-a:focus-visible .dottie-h { display: inline; }
400
+ .dottie-a:hover .dottie-n, .dottie-a.dottie-hover .dottie-n, .dottie-a:focus-visible .dottie-n { display: none; }
401
+ .dottie-a:focus-visible { filter: drop-shadow(0 0 3px var(--dottie-focus, #3b82f6)); }
402
+ .dottie-a:not(.dottie-b):active, .dottie-a.dottie-press:not(.dottie-b) { transform: translate(1px, 1px); }
403
+ .dottie-a .dottie-d { display: none; }
404
+ .dottie-b:active .dottie-n, .dottie-b:active .dottie-h, .dottie-b.dottie-press .dottie-n, .dottie-b.dottie-press .dottie-h { display: none; }
405
+ .dottie-b:active .dottie-d, .dottie-b.dottie-press .dottie-d { display: inline; }
406
+ `;
407
+
326
408
  // src/player.ts
327
409
  function pageIndex(file, spec) {
328
410
  const s = (spec ?? "").trim();
@@ -332,6 +414,22 @@ function pageIndex(file, spec) {
332
414
  const n = Number(s);
333
415
  return Number.isInteger(n) && n >= 1 && n <= file.pages.length ? n - 1 : 0;
334
416
  }
417
+ var STYLE = `
418
+ :host { display: block; position: relative; color: var(--dottie-ink); background: var(--dottie-page);
419
+ font: 14px -apple-system, system-ui, sans-serif; border-radius: var(--dottie-radius, 0); }
420
+ :host([hidden]) { display: none; }
421
+ .stage { display: block; width: 100%; height: auto; }
422
+ .stage svg { display: block; width: 100%; height: auto; margin: 0 auto; }
423
+ .bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px 12px; max-width: 560px; margin: 0 auto; box-sizing: border-box; }
424
+ .bar[hidden] { display: none; }
425
+ button { font: inherit; color: inherit; background: none; border: 1.5px solid currentColor; border-radius: 6px;
426
+ width: 34px; height: 30px; cursor: pointer; flex: none; padding: 0; }
427
+ button:focus-visible, input:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
428
+ input[type=range] { flex: 1; min-width: 60px; accent-color: currentColor; }
429
+ output { font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: right; }
430
+ .scene { min-width: 0; flex: 0 1 auto; text-align: center; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
431
+ .error { padding: 16px; font-family: ui-monospace, Menlo, monospace; white-space: pre-wrap; }
432
+ ${CLICK_STYLE}`;
335
433
 
336
434
  // src/compile.ts
337
435
  var LOADER = `;(function () {
@@ -393,7 +491,7 @@ function compile(source2, runtime, options2 = {}) {
393
491
  }
394
492
 
395
493
  // src/index.ts
396
- var VERSION = "0.5.0";
494
+ var VERSION = "0.5.2";
397
495
 
398
496
  // src/cli.ts
399
497
  var usage = `dottie-compile ${VERSION}: a Dottie drawing as one script that plays it on any page.