@phin-tech/dottie 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /*! dottie.js 0.6.2 | Apache-2.0 | https://github.com/phin-tech/dottie */
1
+ /*! dottie.js 0.7.0 | Apache-2.0 | https://github.com/phin-tech/dottie */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value2) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value: value2 }) : obj[key] = value2;
4
4
  var __export = (target, all) => {
@@ -66,17 +66,71 @@ __export(index_exports, {
66
66
  withRoughness: () => withRoughness
67
67
  });
68
68
 
69
+ // src/state.ts
70
+ var MAX_NAME = 64;
71
+ function isName(name) {
72
+ return name.length <= MAX_NAME && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
73
+ }
74
+ function conditionNames(condition) {
75
+ const out = [];
76
+ for (const any of condition.split("||")) {
77
+ for (const all of any.split("&&")) {
78
+ let term = trim(all);
79
+ if (term.startsWith("!")) term = trim(term.slice(1));
80
+ if (!isName(term)) return null;
81
+ out.push(term);
82
+ }
83
+ }
84
+ return out;
85
+ }
86
+ function holds(condition, state) {
87
+ if (conditionNames(condition) === null) return false;
88
+ return condition.split("||").some((any) => any.split("&&").every((all) => {
89
+ let term = trim(all);
90
+ let negated = false;
91
+ if (term.startsWith("!")) {
92
+ negated = true;
93
+ term = trim(term.slice(1));
94
+ }
95
+ return (state[term] ?? false) !== negated;
96
+ }));
97
+ }
98
+ function stateValues(page, choice) {
99
+ if (choice === "all") return null;
100
+ if (choice === "initial") return { ...page.variables ?? {} };
101
+ return { ...page.variables ?? {}, ...choice };
102
+ }
103
+ function stateHidden(page, choice) {
104
+ const values = stateValues(page, choice);
105
+ const out = /* @__PURE__ */ new Set();
106
+ if (!values) return out;
107
+ for (const e of page.elements) if (e.when !== null && !holds(e.when, values)) out.add(e.id);
108
+ return out;
109
+ }
110
+ function changesState(c) {
111
+ return !!c && (Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0);
112
+ }
113
+ function applyClick(c, state) {
114
+ const out = { ...state };
115
+ for (const [k, v] of Object.entries(c.set ?? {})) out[k] = v;
116
+ for (const k of c.toggle ?? []) out[k] = !(out[k] ?? false);
117
+ return out;
118
+ }
119
+ function trim(s) {
120
+ return s.replace(/^[ \t ]+|[ \t ]+$/g, "");
121
+ }
122
+
69
123
  // src/model.ts
70
124
  var SUPPORTED_VERSION = 2;
71
125
  var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
72
126
  var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
73
127
  var KNOWN_FIELDS = {
74
128
  file: ["version", "pages", "images"],
75
- page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
129
+ page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables"],
76
130
  symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
77
131
  layer: ["id", "name", "visible", "locked"],
78
132
  rgba: ["r", "g", "b", "a"],
79
- style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset"],
133
+ style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor"],
80
134
  element: [
81
135
  "id",
82
136
  "kind",
@@ -100,6 +154,7 @@ var KNOWN_FIELDS = {
100
154
  "anchor",
101
155
  "click",
102
156
  "hover",
157
+ "when",
103
158
  "symbol",
104
159
  "playback",
105
160
  "offset",
@@ -117,15 +172,16 @@ var KNOWN_FIELDS = {
117
172
  extensionRef: ["id", "shape", "version", "params"],
118
173
  normalizedPoint: ["x", "y"],
119
174
  normalizedRect: ["x", "y", "width", "height"],
120
- connector: ["start", "end", "routing", "startHead", "endHead"],
175
+ connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops"],
176
+ waypoint: ["x", "y"],
121
177
  timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
122
178
  track: ["element", "keys", "boil", "follow", "repeats", "march"],
123
179
  keyframe: ["t", "x", "y", "width", "height", "rotation", "strokeWidth", "stroke", "fill", "visible", "reveal", "opacity", "ease"],
124
180
  follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
125
181
  repeat: ["start", "end", "count"],
126
182
  cameraKey: ["t", "x", "y", "width", "height", "ease"],
127
- marker: ["t", "name"],
128
- click: ["scene", "url"],
183
+ marker: ["t", "name", "loop"],
184
+ click: ["scene", "url", "set", "toggle"],
129
185
  hover: ["stroke", "fill", "opacity"]
130
186
  };
131
187
  var MAX_DURATION = 3600;
@@ -199,7 +255,8 @@ var Reader = class {
199
255
  font: str(s.font) || null,
200
256
  gridSize: num(s.gridSize) ?? null,
201
257
  opacity: num(s.opacity) ?? null,
202
- dashOffset: num(s.dashOffset) ?? null
258
+ dashOffset: num(s.dashOffset) ?? null,
259
+ textColor: rgba(s.textColor) ?? null
203
260
  };
204
261
  }
205
262
  endpoint(v) {
@@ -227,7 +284,12 @@ var Reader = class {
227
284
  end: this.endpoint(v.end),
228
285
  routing: routing && ["straight", "curved", "elbow"].includes(routing) ? routing : "straight",
229
286
  startHead: head(v.startHead, "none"),
230
- endHead: head(v.endHead, "arrow")
287
+ endHead: head(v.endHead, "arrow"),
288
+ waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
289
+ this.fields(w, "waypoint");
290
+ return { x: num(w.x) ?? 0, y: num(w.y) ?? 0 };
291
+ }) : null,
292
+ hops: typeof v.hops === "boolean" ? v.hops : null
231
293
  };
232
294
  }
233
295
  image(v, table) {
@@ -278,6 +340,7 @@ var Reader = class {
278
340
  anchor: str(v.anchor) ?? null,
279
341
  click: this.click(v.click),
280
342
  hover: this.hover(v.hover),
343
+ when: str(v.when) ?? null,
281
344
  symbol: uuid(v.symbol) ?? null,
282
345
  playback: this.playback(v.playback),
283
346
  offset: num(v.offset) ?? null,
@@ -313,13 +376,27 @@ var Reader = class {
313
376
  }
314
377
  return { id, shape, version, params };
315
378
  }
379
+ variables(v) {
380
+ if (!isObj(v)) return null;
381
+ const out = {};
382
+ for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
383
+ return Object.keys(out).length > 0 ? out : null;
384
+ }
316
385
  click(v) {
317
386
  if (!isObj(v)) return null;
318
387
  this.fields(v, "click");
319
388
  const url = str(v.url);
320
389
  const safe = url === void 0 ? null : allowedURL(url);
321
390
  if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
322
- return { scene: str(v.scene) ?? null, url: safe };
391
+ let set = null;
392
+ if (isObj(v.set)) {
393
+ set = {};
394
+ for (const [k, b] of Object.entries(v.set)) if (typeof b === "boolean" && isName(k)) set[k] = b;
395
+ if (Object.keys(set).length === 0) set = null;
396
+ }
397
+ const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
398
+ const toggle = raw ? raw.filter(isName) : null;
399
+ return { scene: str(v.scene) ?? null, url: safe, set, toggle: toggle && toggle.length > 0 ? toggle : null };
323
400
  }
324
401
  playback(v) {
325
402
  const s = str(v);
@@ -420,7 +497,7 @@ var Reader = class {
420
497
  if (camera) stableSortBy(camera, (c) => c.t);
421
498
  const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
422
499
  const t = num(m.t);
423
- return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "" }] : [];
500
+ return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
424
501
  }) : null;
425
502
  if (markers) stableSortBy(markers, (m) => m.t);
426
503
  const poster = num(v.poster);
@@ -453,7 +530,8 @@ var Reader = class {
453
530
  elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
454
531
  theme: str(v.theme) ?? null,
455
532
  timeline: this.timeline(v.timeline),
456
- symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
533
+ symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
534
+ variables: this.variables(v.variables)
457
535
  };
458
536
  }
459
537
  };
@@ -504,8 +582,18 @@ function isAutomaticInk(c) {
504
582
  function cloneElement(e) {
505
583
  return {
506
584
  ...e,
507
- style: { ...e.style, stroke: { ...e.style.stroke }, fill: { ...e.style.fill } },
508
- connector: e.connector ? { ...e.connector, start: { ...e.connector.start }, end: { ...e.connector.end } } : null
585
+ style: {
586
+ ...e.style,
587
+ stroke: { ...e.style.stroke },
588
+ fill: { ...e.style.fill },
589
+ textColor: e.style.textColor ? { ...e.style.textColor } : null
590
+ },
591
+ connector: e.connector ? {
592
+ ...e.connector,
593
+ start: { ...e.connector.start },
594
+ end: { ...e.connector.end },
595
+ waypoints: e.connector.waypoints?.map((w) => ({ ...w })) ?? null
596
+ } : null
509
597
  };
510
598
  }
511
599
  function orderedElements(page) {
@@ -935,13 +1023,19 @@ function rotatedBounds(e) {
935
1023
  return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
936
1024
  }
937
1025
  function resolvingInk(e, ink) {
938
- if (!isAutomaticInk(e.style.stroke)) return e;
1026
+ const text = e.style.textColor !== null && isAutomaticInk(e.style.textColor);
1027
+ if (!isAutomaticInk(e.style.stroke) && !text) return e;
939
1028
  const copy = cloneElement(e);
940
1029
  const covers = e.style.fillStyle === "solid" || e.style.fillStyle === "grid";
941
1030
  const onLightFill = covers && e.style.fill.a >= 0.5 && luminance(e.style.fill) > 0.5;
942
- copy.style.stroke = luminance(ink) > 0.5 && onLightFill ? { ...black } : { ...ink };
1031
+ const resolved2 = luminance(ink) > 0.5 && onLightFill ? black : ink;
1032
+ if (isAutomaticInk(e.style.stroke)) copy.style.stroke = { ...resolved2 };
1033
+ if (text) copy.style.textColor = { ...resolved2 };
943
1034
  return copy;
944
1035
  }
1036
+ function labelColor(e) {
1037
+ return e.kind === "text" || e.kind === "note" ? e.style.stroke : e.style.textColor ?? e.style.stroke;
1038
+ }
945
1039
  function shapeContours(e) {
946
1040
  if (e.kind !== "path" || !e.subpaths) return [];
947
1041
  const out = [];
@@ -1035,34 +1129,93 @@ function resolve(ep, other, index) {
1035
1129
  function endpoints(e, index) {
1036
1130
  const c = e.connector;
1037
1131
  if (!c) return [{ x: 0, y: 0 }, { x: 0, y: 0 }];
1038
- return [resolve(c.start, reference(c.end, index), index), resolve(c.end, reference(c.start, index), index)];
1132
+ const w = c.waypoints ?? [];
1133
+ return [
1134
+ resolve(c.start, w.length > 0 ? w[0] : reference(c.end, index), index),
1135
+ resolve(c.end, w.length > 0 ? w[w.length - 1] : reference(c.start, index), index)
1136
+ ];
1039
1137
  }
1040
1138
  function routePoints(r) {
1041
1139
  if (r.kind === "polyline") return r.points;
1042
- const out = [];
1043
- for (let i = 0; i <= 24; i++) {
1044
- const t = i / 24, u = 1 - t;
1045
- out.push({
1046
- x: u * u * u * r.a.x + 3 * u * u * t * r.b.x + 3 * u * t * t * r.c.x + t * t * t * r.d.x,
1047
- y: u * u * u * r.a.y + 3 * u * u * t * r.b.y + 3 * u * t * t * r.c.y + t * t * t * r.d.y
1048
- });
1140
+ const out = [], p = r.points;
1141
+ for (let s = 0; s < Math.floor((p.length - 1) / 3); s++) {
1142
+ const a = p[3 * s], b = p[3 * s + 1], c = p[3 * s + 2], d = p[3 * s + 3];
1143
+ for (let i = s === 0 ? 0 : 1; i <= 24; i++) {
1144
+ const t = i / 24, u = 1 - t;
1145
+ out.push({
1146
+ x: u * u * u * a.x + 3 * u * u * t * b.x + 3 * u * t * t * c.x + t * t * t * d.x,
1147
+ y: u * u * u * a.y + 3 * u * u * t * b.y + 3 * u * t * t * c.y + t * t * t * d.y
1148
+ });
1149
+ }
1049
1150
  }
1050
1151
  return out;
1051
1152
  }
1153
+ var vertical = (p) => p === null ? null : p === "top" || p === "bottom";
1154
+ function simplified(pts) {
1155
+ const out = [];
1156
+ for (const q of pts) {
1157
+ const l = out[out.length - 1];
1158
+ if (l && Math.abs(l.x - q.x) < 0.5 && Math.abs(l.y - q.y) < 0.5) continue;
1159
+ if (out.length >= 2) {
1160
+ const p = out[out.length - 2], m = out[out.length - 1];
1161
+ if (Math.abs(p.x - m.x) < 0.5 && Math.abs(m.x - q.x) < 0.5 || Math.abs(p.y - m.y) < 0.5 && Math.abs(m.y - q.y) < 0.5) {
1162
+ out[out.length - 1] = q;
1163
+ continue;
1164
+ }
1165
+ }
1166
+ out.push(q);
1167
+ }
1168
+ return out.length >= 2 ? out : pts;
1169
+ }
1170
+ function routeThrough(p, c) {
1171
+ switch (c.routing) {
1172
+ case "straight":
1173
+ return { kind: "polyline", points: p };
1174
+ case "curved": {
1175
+ const out = [p[0]];
1176
+ for (let i = 0; i < p.length - 1; i++) {
1177
+ const p0 = p[Math.max(i - 1, 0)], p1 = p[i], p2 = p[i + 1], p3 = p[Math.min(i + 2, p.length - 1)];
1178
+ out.push({ x: p1.x + (p2.x - p0.x) / 6, y: p1.y + (p2.y - p0.y) / 6 });
1179
+ out.push({ x: p2.x - (p3.x - p1.x) / 6, y: p2.y - (p3.y - p1.y) / 6 });
1180
+ out.push(p2);
1181
+ }
1182
+ return { kind: "curve", points: out };
1183
+ }
1184
+ case "elbow": {
1185
+ const a = p[0], first = p[1];
1186
+ let goVertical = vertical(c.start.port) ?? Math.abs(first.y - a.y) > Math.abs(first.x - a.x);
1187
+ const endV = vertical(c.end.port);
1188
+ const out = [a];
1189
+ for (let i = 1; i < p.length; i++) {
1190
+ const q = p[i], cur = out[out.length - 1];
1191
+ const alignedX = Math.abs(cur.x - q.x) < 0.5, alignedY = Math.abs(cur.y - q.y) < 0.5;
1192
+ if (alignedX || alignedY) {
1193
+ out.push(q);
1194
+ if (alignedX !== alignedY) goVertical = !alignedX;
1195
+ continue;
1196
+ }
1197
+ const up = i === p.length - 1 && endV !== null ? !endV : goVertical;
1198
+ out.push(up ? { x: cur.x, y: q.y } : { x: q.x, y: cur.y }, q);
1199
+ goVertical = up;
1200
+ }
1201
+ return { kind: "polyline", points: simplified(out) };
1202
+ }
1203
+ }
1204
+ }
1052
1205
  function route(e, index) {
1053
1206
  const c = e.connector;
1054
1207
  if (!c) return { kind: "polyline", points: [] };
1055
1208
  const [a, b] = endpoints(e, index);
1209
+ if (c.waypoints && c.waypoints.length > 0) return routeThrough([a, ...c.waypoints.map((w) => ({ x: w.x, y: w.y })), b], c);
1056
1210
  const dx = b.x - a.x, dy = b.y - a.y;
1057
1211
  const horizontal = Math.abs(dx) >= Math.abs(dy);
1058
1212
  switch (c.routing) {
1059
1213
  case "straight":
1060
1214
  return { kind: "polyline", points: [a, b] };
1061
1215
  case "curved":
1062
- if (horizontal) return { kind: "cubic", a, b: { x: a.x + dx / 2, y: a.y }, c: { x: b.x - dx / 2, y: b.y }, d: b };
1063
- return { kind: "cubic", a, b: { x: a.x, y: a.y + dy / 2 }, c: { x: b.x, y: b.y - dy / 2 }, d: b };
1216
+ if (horizontal) return { kind: "curve", points: [a, { x: a.x + dx / 2, y: a.y }, { x: b.x - dx / 2, y: b.y }, b] };
1217
+ return { kind: "curve", points: [a, { x: a.x, y: a.y + dy / 2 }, { x: b.x, y: b.y - dy / 2 }, b] };
1064
1218
  case "elbow": {
1065
- const vertical = (p) => p === null ? null : p === "top" || p === "bottom";
1066
1219
  const startV = vertical(c.start.port) ?? !horizontal;
1067
1220
  const endV = vertical(c.end.port) ?? !horizontal;
1068
1221
  if (startV && endV) {
@@ -1642,10 +1795,12 @@ function dashPattern(dash, width) {
1642
1795
  return [0.01, Math.max(5, width * 2.6)];
1643
1796
  }
1644
1797
  }
1645
- function connectorParts(e, index) {
1798
+ function connectorParts(e, index, page) {
1646
1799
  const c = e.connector;
1647
1800
  if (!c) return [new Path(), new Path()];
1648
1801
  const [a, b] = endpoints(e, index);
1802
+ const r = route(e, index);
1803
+ const hops = c.hops === true && page ? hopCrossings(e, r, page, index) : [];
1649
1804
  const key = [
1650
1805
  e.seed,
1651
1806
  e.style.roughness,
@@ -1659,28 +1814,166 @@ function connectorParts(e, index) {
1659
1814
  a.x,
1660
1815
  a.y,
1661
1816
  b.x,
1662
- b.y
1817
+ b.y,
1818
+ JSON.stringify(c.waypoints),
1819
+ JSON.stringify(hops)
1663
1820
  ].join("|");
1664
- return connectors.get(key, () => computeConnector(e, index));
1821
+ return connectors.get(key, () => computeConnector(e, r, hops));
1822
+ }
1823
+ function hopRadius(strokeWidth) {
1824
+ return Math.max(8, strokeWidth * 4);
1665
1825
  }
1666
- function computeConnector(e, index) {
1826
+ function hopSamples(r) {
1827
+ const pts = r.points, out = [];
1828
+ if (r.kind === "polyline") {
1829
+ for (let i = 0; i < pts.length - 1; i++) out.push([pts[i], pts[i + 1]]);
1830
+ return out;
1831
+ }
1832
+ for (let s = 0; s < Math.floor((pts.length - 1) / 3); s++) {
1833
+ const seg = pts.slice(3 * s, 3 * s + 4), samples = [];
1834
+ for (let i = 0; i <= 24; i++) samples.push(cubicPoint(seg, i / 24));
1835
+ out.push(samples);
1836
+ }
1837
+ return out;
1838
+ }
1839
+ function runningLengths(samples) {
1840
+ const out = [0];
1841
+ for (let i = 1; i < samples.length; i++) out.push(out[i - 1] + Math.hypot(samples[i].x - samples[i - 1].x, samples[i].y - samples[i - 1].y));
1842
+ return out;
1843
+ }
1844
+ function cubicPoint(p, t) {
1845
+ const u = 1 - t;
1846
+ return {
1847
+ x: u * u * u * p[0].x + 3 * u * u * t * p[1].x + 3 * u * t * t * p[2].x + t * t * t * p[3].x,
1848
+ y: u * u * u * p[0].y + 3 * u * u * t * p[1].y + 3 * u * t * t * p[2].y + t * t * t * p[3].y
1849
+ };
1850
+ }
1851
+ function subCubic(p, t0, t1) {
1852
+ const split = (q, t) => {
1853
+ const lerp = (a, b) => ({ x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t });
1854
+ const ab = lerp(q[0], q[1]), bc = lerp(q[1], q[2]), cd = lerp(q[2], q[3]);
1855
+ const abc = lerp(ab, bc), bcd = lerp(bc, cd), m = lerp(abc, bcd);
1856
+ return [[q[0], ab, abc, m], [m, bcd, cd, q[3]]];
1857
+ };
1858
+ const left = split(p, t1)[0];
1859
+ return t1 > 0 ? split(left, t0 / t1)[1] : [p[0], p[0], p[0], p[0]];
1860
+ }
1861
+ function hopCrossings(e, r, page, index) {
1862
+ const segments = hopSamples(r);
1863
+ if (segments.length === 0) return [];
1864
+ const visible = new Set(page.layers.filter((l) => l.visible).map((l) => l.id));
1865
+ const beneath = [];
1866
+ for (const o of orderedElements(page)) {
1867
+ if (o.id === e.id) break;
1868
+ if (o.kind !== "connector" || !visible.has(o.layerID)) continue;
1869
+ beneath.push(routePoints(route(o, index)));
1870
+ }
1871
+ const rad = hopRadius(e.style.strokeWidth);
1872
+ const out = [];
1873
+ for (const samples of segments) {
1874
+ const along = runningLengths(samples);
1875
+ const length = along[along.length - 1];
1876
+ const found = [];
1877
+ for (let i = 0; i < samples.length - 1; i++) {
1878
+ const p = samples[i], q = samples[i + 1];
1879
+ const dx = q.x - p.x, dy = q.y - p.y;
1880
+ for (const line2 of beneath) {
1881
+ for (let k = 0; k < line2.length - 1; k++) {
1882
+ const a = line2[k], b = line2[k + 1];
1883
+ const ex = b.x - a.x, ey = b.y - a.y;
1884
+ const det = dx * ey - dy * ex;
1885
+ if (Math.abs(det) < 1e-9) continue;
1886
+ const wx = a.x - p.x, wy = a.y - p.y;
1887
+ const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
1888
+ if (t > 0 && t < 1 && s >= 0 && s <= 1) found.push(along[i] + t * (along[i + 1] - along[i]));
1889
+ }
1890
+ }
1891
+ }
1892
+ const kept = [];
1893
+ for (const at of found.sort((x, y) => x - y)) {
1894
+ if (at < rad + 2 || at > length - rad - 2) continue;
1895
+ if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
1896
+ kept.push(at);
1897
+ }
1898
+ out.push(kept);
1899
+ }
1900
+ return out;
1901
+ }
1902
+ function computeConnector(e, r, hops) {
1667
1903
  const c = e.connector;
1668
1904
  const rng = new SeededRNG(e.seed);
1669
1905
  const rough = e.style.roughness, bow = e.style.bowing;
1670
1906
  const path = new Path(), solid = new Path();
1671
- const r = route(e, index);
1907
+ const w = rough * 3;
1908
+ const j = () => rng.signed() * w;
1909
+ const hop = (s, t, u, rad2) => {
1910
+ let n = { x: u.y, y: -u.x };
1911
+ if (n.y > 1e-9 || Math.abs(n.y) <= 1e-9 && n.x > 0) n = { x: -n.x, y: -n.y };
1912
+ const k = rad2 * 0.5523;
1913
+ const m = { x: (s.x + t.x) / 2, y: (s.y + t.y) / 2 };
1914
+ const top = { x: m.x + n.x * rad2, y: m.y + n.y * rad2 };
1915
+ path.moveTo(s);
1916
+ const a1x = s.x + n.x * k + j(), a1y = s.y + n.y * k + j(), a2x = top.x - u.x * k + j(), a2y = top.y - u.y * k + j();
1917
+ path.curveTo(top, { x: a1x, y: a1y }, { x: a2x, y: a2y });
1918
+ const b1x = top.x + u.x * k + j(), b1y = top.y + u.y * k + j(), b2x = t.x + n.x * k + j(), b2y = t.y + n.y * k + j();
1919
+ path.curveTo(t, { x: b1x, y: b1y }, { x: b2x, y: b2y });
1920
+ };
1921
+ const rad = hopRadius(e.style.strokeWidth);
1672
1922
  if (r.kind === "polyline") {
1673
1923
  const pts2 = r.points;
1924
+ const straight = (p, q) => {
1925
+ if (rough <= 0) path.moveTo(p).lineTo(q);
1926
+ else line(p, q, rng, rough, bow, true, path);
1927
+ };
1674
1928
  for (let i = 0; i < Math.max(pts2.length - 1, 0); i++) {
1675
- if (rough <= 0) path.moveTo(pts2[i]).lineTo(pts2[i + 1]);
1676
- else line(pts2[i], pts2[i + 1], rng, rough, bow, true, path);
1929
+ const p = pts2[i], q = pts2[i + 1];
1930
+ const length = Math.hypot(q.x - p.x, q.y - p.y);
1931
+ const u = length > 0 ? { x: (q.x - p.x) / length, y: (q.y - p.y) / length } : { x: 0, y: 0 };
1932
+ const at = (d) => ({ x: p.x + u.x * d, y: p.y + u.y * d });
1933
+ let from = p;
1934
+ for (const d of i < hops.length ? hops[i] : []) {
1935
+ const s = at(d - rad), t = at(d + rad);
1936
+ straight(from, s);
1937
+ hop(s, t, u, rad);
1938
+ from = t;
1939
+ }
1940
+ straight(from, q);
1677
1941
  }
1678
1942
  } else {
1679
- path.moveTo(r.a);
1680
- const w = rough * 3;
1681
- const j = () => rng.signed() * w;
1682
- const c1x = r.b.x + j(), c1y = r.b.y + j(), c2x = r.c.x + j(), c2y = r.c.y + j();
1683
- path.curveTo(r.d, { x: c1x, y: c1y }, { x: c2x, y: c2y });
1943
+ const pts2 = r.points;
1944
+ const samples = hopSamples(r);
1945
+ path.moveTo(pts2[0]);
1946
+ for (let s = 0; s < Math.floor((pts2.length - 1) / 3); s++) {
1947
+ const b = pts2[3 * s + 1], c2 = pts2[3 * s + 2], d = pts2[3 * s + 3];
1948
+ const c1x = b.x + j(), c1y = b.y + j(), c2x = c2.x + j(), c2y = c2.y + j();
1949
+ const here = s < hops.length ? hops[s] : [];
1950
+ if (here.length === 0) {
1951
+ path.curveTo(d, { x: c1x, y: c1y }, { x: c2x, y: c2y });
1952
+ continue;
1953
+ }
1954
+ const cubic = [pts2[3 * s], { x: c1x, y: c1y }, { x: c2x, y: c2y }, d];
1955
+ const along = runningLengths(samples[s]);
1956
+ const param = (at) => {
1957
+ let i = 0;
1958
+ while (i < along.length - 2 && along[i + 1] < at) i++;
1959
+ const span = along[i + 1] - along[i];
1960
+ return (i + (span > 0 ? (at - along[i]) / span : 0)) / 24;
1961
+ };
1962
+ let from = 0;
1963
+ for (const at of here) {
1964
+ const t0 = param(at - rad), t1 = param(at + rad);
1965
+ const piece = subCubic(cubic, from, t0);
1966
+ if (s > 0 || from > 0) path.moveTo(piece[0]);
1967
+ path.curveTo(piece[3], piece[1], piece[2]);
1968
+ const a = cubicPoint(cubic, t0), z = cubicPoint(cubic, t1);
1969
+ const width = Math.hypot(z.x - a.x, z.y - a.y);
1970
+ const u = width > 0 ? { x: (z.x - a.x) / width, y: (z.y - a.y) / width } : { x: 0, y: 0 };
1971
+ hop(a, z, u, width / 2);
1972
+ from = t1;
1973
+ }
1974
+ const rest = subCubic(cubic, from, 1);
1975
+ path.moveTo(rest[0]).curveTo(rest[3], rest[1], rest[2]);
1976
+ }
1684
1977
  }
1685
1978
  const pts = routePoints(r);
1686
1979
  const size = Math.max(10, e.style.strokeWidth * 5);
@@ -1702,10 +1995,10 @@ function computeConnector(e, index) {
1702
1995
  solid.moveTo(tip).lineTo(w1).lineTo(w2).close();
1703
1996
  break;
1704
1997
  case "dot": {
1705
- const rad = size * 0.32;
1998
+ const rad2 = size * 0.32;
1706
1999
  const ang = Math.atan2(tip.y - from.y, tip.x - from.x);
1707
- const cx = tip.x - rad * Math.cos(ang), cy = tip.y - rad * Math.sin(ang);
1708
- solid.addEllipse({ x: cx - rad, y: cy - rad, width: 2 * rad, height: 2 * rad });
2000
+ const cx = tip.x - rad2 * Math.cos(ang), cy = tip.y - rad2 * Math.sin(ang);
2001
+ solid.addEllipse({ x: cx - rad2, y: cy - rad2, width: 2 * rad2, height: 2 * rad2 });
1709
2002
  break;
1710
2003
  }
1711
2004
  case "bar": {
@@ -1908,17 +2201,20 @@ function trackMap(tl) {
1908
2201
  for (const t of tl.tracks) if (!m.has(t.element)) m.set(t.element, t);
1909
2202
  return m;
1910
2203
  }
1911
- function resolved(page, t) {
1912
- return expandingSymbols(posed(page, t), clampTime(t));
2204
+ function resolved(page, t, state = "initial") {
2205
+ return expandingSymbols(posed(page, t, state), clampTime(t));
1913
2206
  }
1914
- function posed(page, t) {
1915
- const timeline = page.timeline;
2207
+ var EMPTY_TIMELINE = { fps: 12, duration: 0, boil: null, settle: null, poster: null, tracks: [], camera: null, markers: null };
2208
+ function posed(page, t, state = "initial") {
2209
+ const off = stateHidden(page, state);
2210
+ const timeline = page.timeline ?? (off.size === 0 ? null : EMPTY_TIMELINE);
1916
2211
  if (!timeline) return page;
1917
2212
  t = clampTime(t);
1918
2213
  const tracks = trackMap(timeline);
1919
2214
  const reseed = timeline.boil === null ? null : Math.trunc(Math.floor(t * clampFPS(timeline.fps) + 1e-9) / Math.max(1, timeline.boil)) % BOIL_DRAWINGS >>> 0;
1920
2215
  const hidden = /* @__PURE__ */ new Set();
1921
2216
  for (const tr of tracks.values()) if (!isVisible(tr, t)) hidden.add(tr.element);
2217
+ for (const id of off) hidden.add(id);
1922
2218
  let moving = null;
1923
2219
  if (timeline.settle === true) {
1924
2220
  moving = /* @__PURE__ */ new Set();
@@ -1957,7 +2253,7 @@ function posed(page, t) {
1957
2253
  elements = elements.map((e) => {
1958
2254
  const c = e.connector;
1959
2255
  if (!c || ![c.start.elementID, c.end.elementID].some((id) => id !== null && hidden.has(id))) return e;
1960
- if (!(tracks.get(e.id)?.keys.some((k) => k.visible !== void 0) ?? false)) {
2256
+ if (!(tracks.get(e.id)?.keys.some((k) => k.visible !== void 0) ?? false) || [c.start.elementID, c.end.elementID].some((id) => id !== null && off.has(id))) {
1961
2257
  gone.add(e.id);
1962
2258
  return e;
1963
2259
  }
@@ -1973,7 +2269,7 @@ function posterTime(page) {
1973
2269
  return page.timeline ? page.timeline.poster ?? page.timeline.duration : 0;
1974
2270
  }
1975
2271
  function still(page) {
1976
- return page.timeline ? resolved(page, posterTime(page)) : expandingSymbols(page, 0);
2272
+ return page.timeline ? resolved(page, posterTime(page)) : expandingSymbols(posed(page, 0), 0);
1977
2273
  }
1978
2274
  function cameraRect(tl, t) {
1979
2275
  const keys = tl.camera ?? [];
@@ -2082,7 +2378,7 @@ function filled(s, args) {
2082
2378
  }) };
2083
2379
  }
2084
2380
  function symbolPage(s) {
2085
- return { id: s.id, name: s.name, layers: s.layers, elements: s.elements, theme: null, timeline: s.timeline, symbols: null };
2381
+ return { id: s.id, name: s.name, layers: s.layers, elements: s.elements, theme: null, timeline: s.timeline, symbols: null, variables: null };
2086
2382
  }
2087
2383
  function hasInstances(page) {
2088
2384
  return page.elements.some((e) => e.kind === "symbol");
@@ -2172,7 +2468,12 @@ function placed(inner, instance, symbol) {
2172
2468
  const q = point(p);
2173
2469
  return { elementID: null, port: p.port, x: q.x, y: q.y };
2174
2470
  };
2175
- e.connector = { ...inner.connector, start: end(inner.connector.start), end: end(inner.connector.end) };
2471
+ e.connector = {
2472
+ ...inner.connector,
2473
+ start: end(inner.connector.start),
2474
+ end: end(inner.connector.end),
2475
+ waypoints: inner.connector.waypoints?.map(point) ?? null
2476
+ };
2176
2477
  }
2177
2478
  return e;
2178
2479
  }
@@ -2206,16 +2507,21 @@ function clickTargets(page, file) {
2206
2507
  let pageOf = null;
2207
2508
  for (const e of page.elements) {
2208
2509
  const c = e.click;
2510
+ const state = c && changesState(c) ? { ...c.set ? { set: c.set } : {}, ...c.toggle ? { toggle: c.toggle } : {} } : null;
2209
2511
  if (c?.scene) {
2210
2512
  const i = sceneNamed(c.scene, markers);
2211
2513
  if (i >= 0) {
2212
- out.set(e.id, { scene: i });
2514
+ out.set(e.id, { scene: i, ...state });
2213
2515
  continue;
2214
2516
  }
2215
2517
  }
2216
2518
  const url = (c?.url ? allowedURL(c.url) : null) ?? webLink(e.url);
2217
2519
  if (url) {
2218
- out.set(e.id, { url });
2520
+ out.set(e.id, { url, ...state });
2521
+ continue;
2522
+ }
2523
+ if (state) {
2524
+ out.set(e.id, { state: true, ...state });
2219
2525
  continue;
2220
2526
  }
2221
2527
  if (e.link && file && file.pages.length > 1) {
@@ -2948,7 +3254,7 @@ function svgParts(page, background, fixed, links = false) {
2948
3254
  if (!visible.has(e.layerID)) continue;
2949
3255
  const alpha = Math.min(Math.max(e.style.opacity ?? 1, 0), 1);
2950
3256
  if (alpha <= 0) continue;
2951
- let body = svgElement(e, index, bg);
3257
+ let body = svgElement(e, index, bg, d);
2952
3258
  if (angle(e) !== 0 && e.kind !== "connector") {
2953
3259
  body = `<g transform="rotate(${fmt(angle(e) * 180 / Math.PI)} ${fmt(midX(frame(e)))} ${fmt(midY(frame(e)))})">
2954
3260
  ${body}</g>
@@ -2983,7 +3289,7 @@ function typed(s, progress) {
2983
3289
  function revealProgress(e) {
2984
3290
  return e.reveal !== null && e.reveal < 1 ? Math.max(0, e.reveal) : null;
2985
3291
  }
2986
- function svgElement(e, index, labelBackground) {
3292
+ function svgElement(e, index, labelBackground, page) {
2987
3293
  const stroke = color(e.style.stroke);
2988
3294
  let strokeAttrs = `stroke="${stroke}" stroke-width="${fmt(e.style.strokeWidth)}" stroke-linecap="round" stroke-linejoin="round"`;
2989
3295
  const dash = dashPattern(e.style.dash, e.style.strokeWidth);
@@ -2994,7 +3300,7 @@ function svgElement(e, index, labelBackground) {
2994
3300
  if (r !== null) {
2995
3301
  if (e.kind === "text") text = typed(text ?? "", r);
2996
3302
  else if (e.kind === "connector") {
2997
- return `<path d="${connectorParts(e, index)[0].trimmed(r).data()}" fill="none" ${strokeAttrs}/>
3303
+ return `<path d="${connectorParts(e, index, page)[0].trimmed(r).data()}" fill="none" ${strokeAttrs}/>
2998
3304
  `;
2999
3305
  } else {
3000
3306
  return `<path d="${outline(e).trimmed(r).data()}" fill="none" ${strokeAttrs}/>
@@ -3003,7 +3309,7 @@ function svgElement(e, index, labelBackground) {
3003
3309
  }
3004
3310
  switch (e.kind) {
3005
3311
  case "connector": {
3006
- const [line2, filled2] = connectorParts(e, index);
3312
+ const [line2, filled2] = connectorParts(e, index, page);
3007
3313
  let s = `<path d="${line2.data()}" fill="none" ${strokeAttrs}/>
3008
3314
  `;
3009
3315
  if (!filled2.isEmpty) s += `<path d="${filled2.data()}" fill="${stroke}" stroke="none"/>
@@ -3086,7 +3392,7 @@ function textElement(text, f, e, centered) {
3086
3392
  const total = lineHeight * lines.length;
3087
3393
  const x = centered ? midX(f) : f.x;
3088
3394
  const top = centered ? midY(f) - total / 2 : f.y;
3089
- let s = `<text x="${fmt(x)}" font-family="${cssFamily(e.style.font)}" font-size="${fmt(size)}" fill="${color(e.style.stroke)}"${centered ? ` text-anchor="middle"` : ""}>`;
3395
+ let s = `<text x="${fmt(x)}" font-family="${cssFamily(e.style.font)}" font-size="${fmt(size)}" fill="${color(labelColor(e))}"${centered ? ` text-anchor="middle"` : ""}>`;
3090
3396
  lines.forEach((line2, i) => {
3091
3397
  s += `<tspan x="${fmt(x)}" y="${fmt(top + lineHeight * (i + 0.8))}">${escapeXML(line2)}</tspan>`;
3092
3398
  });
@@ -3129,6 +3435,10 @@ var Playback = class {
3129
3435
  __publicField(this, "plan");
3130
3436
  __publicField(this, "scenes");
3131
3437
  __publicField(this, "sceneNames");
3438
+ /** Whether each scene loops (Marker.loop). */
3439
+ __publicField(this, "sceneLoops");
3440
+ /** The page's variables now: their starting values until clicks change them. */
3441
+ __publicField(this, "state");
3132
3442
  __publicField(this, "background");
3133
3443
  __publicField(this, "ink");
3134
3444
  /** What clicking each clickable element does, by element id. */
@@ -3188,6 +3498,8 @@ var Playback = class {
3188
3498
  };
3189
3499
  this.scenes = (page.timeline?.markers ?? []).map((m) => near(m.t));
3190
3500
  this.sceneNames = (page.timeline?.markers ?? []).map((m) => m.name);
3501
+ this.sceneLoops = (page.timeline?.markers ?? []).map((m) => m.loop === true);
3502
+ this.state = { ...page.variables ?? {} };
3191
3503
  const theme = themeNamed(page.theme);
3192
3504
  this.background = theme?.background ?? WHITE;
3193
3505
  this.ink = theme?.ink ?? { r: 0.12, g: 0.12, b: 0.13, a: 1 };
@@ -3238,7 +3550,7 @@ var Playback = class {
3238
3550
  const times = /* @__PURE__ */ new Map();
3239
3551
  for (const [id, start] of this.replayStarts) times.set(id, Math.min((now - start) / 1e3, this.replays.get(id) ?? 0));
3240
3552
  const tc = clampTime(t);
3241
- const pose = posed(this.page, tc);
3553
+ const pose = posed(this.page, tc, this.state);
3242
3554
  const frame2 = expandingSymbols(pose, tc, true, times);
3243
3555
  const { parts } = svgParts(frame2, this.background, r);
3244
3556
  const owned = (m, parts2) => {
@@ -3360,6 +3672,20 @@ ${h}</g>
3360
3672
  });
3361
3673
  return s;
3362
3674
  }
3675
+ /** The frame after scene `s`'s last: the next scene's, or the frame count after the last scene. */
3676
+ sceneEnd(s) {
3677
+ return this.scenes.find((f, j) => j > s && f > this.scenes[s]) ?? this.frameCount;
3678
+ }
3679
+ /** Where playing goes from frame `i`, and whether it stops there: on into a looping scene, round to the start of
3680
+ * the looping scene `i` ends, or a stop at a scene or the end (`loops`: whether the timeline loops at its end). */
3681
+ step(i, loops) {
3682
+ const next = i + 1;
3683
+ const s = this.sceneAt(i);
3684
+ if (s >= 0 && this.sceneLoops[s] && next >= this.sceneEnd(s)) return { frame: this.scenes[s], stop: false };
3685
+ if (next >= this.frameCount) return { frame: 0, stop: !loops || this.scenes.length > 0 };
3686
+ const entered = this.scenes.indexOf(next);
3687
+ return { frame: next, stop: entered >= 0 && !this.sceneLoops[entered] };
3688
+ }
3363
3689
  };
3364
3690
  var ElementBase = typeof HTMLElement === "undefined" ? class {
3365
3691
  } : HTMLElement;
@@ -3513,7 +3839,7 @@ var DottiePlayer = class extends ElementBase {
3513
3839
  this.setPlaying(false);
3514
3840
  return;
3515
3841
  }
3516
- this.setPlaying(!this.hasAttribute("no-autoplay") && !p.scenes.includes(0));
3842
+ this.setPlaying(!this.hasAttribute("no-autoplay") && (!p.scenes.includes(0) || p.sceneLoops[p.scenes.indexOf(0)]));
3517
3843
  }
3518
3844
  // MARK: Playback
3519
3845
  get paused() {
@@ -3539,11 +3865,13 @@ var DottiePlayer = class extends ElementBase {
3539
3865
  pause() {
3540
3866
  this.setPlaying(false);
3541
3867
  }
3542
- /** Plays on to the next scene (from the start after the last). */
3868
+ /** Plays on to the next scene (from the start after the last). Out of a looping scene it jumps to the next. */
3543
3869
  nextScene() {
3544
3870
  const p = this.playback;
3545
3871
  if (!p) return;
3546
- if (p.frame >= p.frameCount - 1) p.show(0);
3872
+ const s = p.sceneAt(p.frame);
3873
+ if (s >= 0 && p.sceneLoops[s]) p.show(s + 1 < p.scenes.length ? p.scenes[s + 1] : 0);
3874
+ else if (p.frame >= p.frameCount - 1) p.show(0);
3547
3875
  this.setPlaying(true);
3548
3876
  }
3549
3877
  /** Back to the start of this scene, or the one before if already there. */
@@ -3572,6 +3900,8 @@ var DottiePlayer = class extends ElementBase {
3572
3900
  this.scheduleReplay();
3573
3901
  }
3574
3902
  if (!click) return;
3903
+ if (click.set || click.toggle) this.setState(applyClick({ scene: null, url: null, set: click.set ?? null, toggle: click.toggle ?? null }, p.state));
3904
+ if ("state" in click) return;
3575
3905
  if ("url" in click) {
3576
3906
  if (/^(https?:|mailto:)/i.test(click.url)) window.open(click.url, "_blank", "noopener");
3577
3907
  } else if ("page" in click) {
@@ -3579,13 +3909,27 @@ var DottiePlayer = class extends ElementBase {
3579
3909
  } else if (click.scene >= 0 && click.scene < p.scenes.length) {
3580
3910
  if (reduced || this.hasAttribute("at") || p.frameCount < 2) {
3581
3911
  this.setPlaying(false);
3582
- p.show(click.scene + 1 < p.scenes.length ? p.scenes[click.scene + 1] : p.frameCount - 1);
3912
+ p.show(p.sceneLoops[click.scene] ? p.scenes[click.scene] : click.scene + 1 < p.scenes.length ? p.scenes[click.scene + 1] : p.frameCount - 1);
3583
3913
  } else {
3584
3914
  p.show(p.scenes[click.scene]);
3585
3915
  this.setPlaying(true);
3586
3916
  }
3587
3917
  }
3588
3918
  }
3919
+ /** The page's variables now. */
3920
+ get variables() {
3921
+ return { ...this.playback?.state ?? {} };
3922
+ }
3923
+ /** Gives the page's variables these values (the others keep theirs), redraws, and fires `state`. */
3924
+ setState(values) {
3925
+ const p = this.playback;
3926
+ if (!p) return;
3927
+ const next = { ...p.state, ...values };
3928
+ if (Object.keys(next).every((k) => next[k] === p.state[k])) return;
3929
+ p.state = next;
3930
+ p.redraw();
3931
+ this.dispatchEvent(new CustomEvent("state", { detail: { variables: { ...next } } }));
3932
+ }
3589
3933
  /** Redraws, while the page itself stands still, as long as a clicked copy is playing through. */
3590
3934
  scheduleReplay() {
3591
3935
  if (this.replayRaf) return;
@@ -3611,7 +3955,8 @@ var DottiePlayer = class extends ElementBase {
3611
3955
  schedule() {
3612
3956
  if (this.playing && this.visible && !this.raf && this.isConnected) this.raf = requestAnimationFrame((now) => this.tick(now));
3613
3957
  }
3614
- /** Steps whole frames at the timeline's rate; with scenes, playing stops at each one and at the end. */
3958
+ /** Steps whole frames at the timeline's rate; with scenes, playing stops at each one and at the end, except that a
3959
+ * looping scene plays round (Playback.step). */
3615
3960
  tick(now) {
3616
3961
  this.raf = 0;
3617
3962
  const p = this.playback;
@@ -3622,14 +3967,14 @@ var DottiePlayer = class extends ElementBase {
3622
3967
  let advanced = false;
3623
3968
  while (this.playing && this.acc >= step) {
3624
3969
  this.acc -= step;
3625
- const next = p.frame + 1;
3626
- if (next >= p.frameCount && (p.scenes.length || this.hasAttribute("no-loop"))) {
3970
+ const { frame: frame2, stop } = p.step(p.frame, !this.hasAttribute("no-loop"));
3971
+ if (stop && frame2 === 0 && p.frame + 1 >= p.frameCount) {
3627
3972
  this.setPlaying(false);
3628
3973
  break;
3629
3974
  }
3630
- p.frame = next % p.frameCount;
3975
+ p.frame = frame2;
3631
3976
  advanced = true;
3632
- if (p.scenes.includes(p.frame)) {
3977
+ if (stop) {
3633
3978
  this.setPlaying(false);
3634
3979
  break;
3635
3980
  }
@@ -3735,6 +4080,7 @@ function inspectFrame(page, t) {
3735
4080
  d.strokeWidth = r4(e.style.strokeWidth);
3736
4081
  d.stroke = rgba2(e.style.stroke);
3737
4082
  d.fill = rgba2(e.style.fill);
4083
+ if (e.style.textColor !== null) d.textColor = rgba2(e.style.textColor);
3738
4084
  if (e.kind === "connector") {
3739
4085
  const [a, b] = endpoints(e, index);
3740
4086
  d.start = pt(a);
@@ -3778,7 +4124,7 @@ function render(source, options = {}) {
3778
4124
  function isFile(v) {
3779
4125
  return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
3780
4126
  }
3781
- var VERSION = "0.6.2";
4127
+ var VERSION = "0.7.0";
3782
4128
 
3783
4129
  // src/player-entry.ts
3784
4130
  definePlayer();