@phin-tech/dottie 0.9.0 → 0.11.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.
- package/PARITY.md +5 -0
- package/README.md +15 -0
- package/dist/compile.js +123 -15
- package/dist/dottie-compile.mjs +131 -17
- package/dist/dottie-logic.min.js +2171 -0
- package/dist/dottie-player.js +819 -209
- package/dist/dottie-player.min.js +55 -53
- package/dist/dottie-sim.min.js +5 -4
- package/dist/dottie.js +817 -209
- package/dist/types/clicks.d.ts +3 -2
- package/dist/types/compile.d.ts +3 -0
- package/dist/types/elbow.d.ts +18 -0
- package/dist/types/expression.d.ts +51 -0
- package/dist/types/flows.d.ts +2 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/logic-entry.d.ts +1 -0
- package/dist/types/logic-loader.d.ts +9 -0
- package/dist/types/logic-polyfill.d.ts +2 -0
- package/dist/types/logic.d.ts +32 -0
- package/dist/types/measure.d.ts +15 -3
- package/dist/types/model.d.ts +42 -3
- package/dist/types/player.d.ts +8 -3
- package/dist/types/state.d.ts +14 -13
- package/dist/types/theme.d.ts +13 -1
- package/package.json +3 -1
package/dist/dottie-player.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! dottie.js 0.
|
|
1
|
+
/*! dottie.js 0.11.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) => {
|
|
@@ -44,6 +44,8 @@ __export(index_exports, {
|
|
|
44
44
|
luminance: () => luminance,
|
|
45
45
|
orderedElements: () => orderedElements,
|
|
46
46
|
pageIndex: () => pageIndex,
|
|
47
|
+
pageTheme: () => pageTheme,
|
|
48
|
+
paletteColor: () => paletteColor,
|
|
47
49
|
parseDottie: () => parseDottie,
|
|
48
50
|
plan: () => plan,
|
|
49
51
|
posterTime: () => posterTime,
|
|
@@ -66,82 +68,332 @@ __export(index_exports, {
|
|
|
66
68
|
withRoughness: () => withRoughness
|
|
67
69
|
});
|
|
68
70
|
|
|
69
|
-
// src/
|
|
71
|
+
// src/expression.ts
|
|
72
|
+
var METRIC_NAMES = ["throughput", "load", "delay", "backlog", "depth", "inFlight", "transit", "backup"];
|
|
73
|
+
var FUNCTIONS = { min: null, max: null, abs: 1, floor: 1, ceil: 1, round: 1, clamp: 3 };
|
|
70
74
|
var MAX_NAME = 64;
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
var truthy = (v) => typeof v === "boolean" ? v : v !== 0 && !Number.isNaN(v);
|
|
76
|
+
var num = (v) => typeof v === "boolean" ? v ? 1 : 0 : v;
|
|
77
|
+
var parsed = /* @__PURE__ */ new Map();
|
|
78
|
+
function parseExpr(text) {
|
|
79
|
+
const hit = parsed.get(text);
|
|
80
|
+
if (hit !== void 0) return hit;
|
|
81
|
+
const p = new Parser(text);
|
|
82
|
+
let e = p.or();
|
|
83
|
+
if (e && !p.atEnd()) e = null;
|
|
84
|
+
if (parsed.size > 4096) parsed.clear();
|
|
85
|
+
parsed.set(text, e);
|
|
86
|
+
return e;
|
|
73
87
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
88
|
+
function evaluate(e, state, metric = null) {
|
|
89
|
+
switch (e.k) {
|
|
90
|
+
case "lit":
|
|
91
|
+
return e.v;
|
|
92
|
+
case "name":
|
|
93
|
+
return state[e.name] ?? false;
|
|
94
|
+
case "metric":
|
|
95
|
+
return metric?.(e.metric, e.target) ?? NaN;
|
|
96
|
+
case "not":
|
|
97
|
+
return !truthy(evaluate(e.e, state, metric));
|
|
98
|
+
case "neg":
|
|
99
|
+
return -num(evaluate(e.e, state, metric));
|
|
100
|
+
case "call": {
|
|
101
|
+
const xs = e.args.map((a) => num(evaluate(a, state, metric)));
|
|
102
|
+
const fold = (better) => {
|
|
103
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
104
|
+
let out = xs[0];
|
|
105
|
+
for (const x of xs.slice(1)) if (better(x, out)) out = x;
|
|
106
|
+
return out;
|
|
107
|
+
};
|
|
108
|
+
switch (e.fn) {
|
|
109
|
+
case "min":
|
|
110
|
+
return fold((a, b) => a < b);
|
|
111
|
+
case "max":
|
|
112
|
+
return fold((a, b) => a > b);
|
|
113
|
+
case "abs":
|
|
114
|
+
return Math.abs(xs[0]);
|
|
115
|
+
case "floor":
|
|
116
|
+
return Math.floor(xs[0]);
|
|
117
|
+
case "ceil":
|
|
118
|
+
return Math.ceil(xs[0]);
|
|
119
|
+
case "round":
|
|
120
|
+
return Math.floor(xs[0] + 0.5);
|
|
121
|
+
default:
|
|
122
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
123
|
+
return xs[0] < xs[1] ? xs[1] : xs[0] > xs[2] ? xs[2] : xs[0];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
case "bin": {
|
|
127
|
+
if (e.op === "&&") return truthy(evaluate(e.l, state, metric)) && truthy(evaluate(e.r, state, metric));
|
|
128
|
+
if (e.op === "||") return truthy(evaluate(e.l, state, metric)) || truthy(evaluate(e.r, state, metric));
|
|
129
|
+
const a = evaluate(e.l, state, metric), b = evaluate(e.r, state, metric);
|
|
130
|
+
const x = num(a), y = num(b);
|
|
131
|
+
switch (e.op) {
|
|
132
|
+
case "+":
|
|
133
|
+
return x + y;
|
|
134
|
+
case "-":
|
|
135
|
+
return x - y;
|
|
136
|
+
case "*":
|
|
137
|
+
return x * y;
|
|
138
|
+
case "/":
|
|
139
|
+
return x / y;
|
|
140
|
+
case "%":
|
|
141
|
+
return x % y;
|
|
142
|
+
}
|
|
143
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return false;
|
|
144
|
+
switch (e.op) {
|
|
145
|
+
case "<":
|
|
146
|
+
return x < y;
|
|
147
|
+
case "<=":
|
|
148
|
+
return x <= y;
|
|
149
|
+
case ">":
|
|
150
|
+
return x > y;
|
|
151
|
+
case ">=":
|
|
152
|
+
return x >= y;
|
|
153
|
+
case "==":
|
|
154
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a === b : x === y;
|
|
155
|
+
default:
|
|
156
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a !== b : x !== y;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
111
159
|
}
|
|
112
160
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
161
|
+
function exprNames(e) {
|
|
162
|
+
const out = [];
|
|
163
|
+
const walk = (x) => {
|
|
164
|
+
switch (x.k) {
|
|
165
|
+
case "name":
|
|
166
|
+
if (!out.includes(x.name)) out.push(x.name);
|
|
167
|
+
break;
|
|
168
|
+
case "not":
|
|
169
|
+
case "neg":
|
|
170
|
+
walk(x.e);
|
|
171
|
+
break;
|
|
172
|
+
case "bin":
|
|
173
|
+
walk(x.l);
|
|
174
|
+
walk(x.r);
|
|
175
|
+
break;
|
|
176
|
+
case "call":
|
|
177
|
+
x.args.forEach(walk);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
walk(e);
|
|
182
|
+
return out;
|
|
115
183
|
}
|
|
116
|
-
function
|
|
184
|
+
function exprMetrics(e) {
|
|
117
185
|
const out = [];
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
186
|
+
const walk = (x) => {
|
|
187
|
+
switch (x.k) {
|
|
188
|
+
case "metric":
|
|
189
|
+
out.push({ metric: x.metric, target: x.target });
|
|
190
|
+
break;
|
|
191
|
+
case "not":
|
|
192
|
+
case "neg":
|
|
193
|
+
walk(x.e);
|
|
194
|
+
break;
|
|
195
|
+
case "bin":
|
|
196
|
+
walk(x.l);
|
|
197
|
+
walk(x.r);
|
|
198
|
+
break;
|
|
199
|
+
case "call":
|
|
200
|
+
x.args.forEach(walk);
|
|
201
|
+
break;
|
|
125
202
|
}
|
|
126
|
-
}
|
|
203
|
+
};
|
|
204
|
+
walk(e);
|
|
127
205
|
return out;
|
|
128
206
|
}
|
|
207
|
+
var isDigit = (c) => c !== void 0 && c >= "0" && c <= "9";
|
|
208
|
+
var isLetter = (c) => c !== void 0 && (c === "_" || c >= "a" && c <= "z" || c >= "A" && c <= "Z");
|
|
209
|
+
var Parser = class {
|
|
210
|
+
constructor(text) {
|
|
211
|
+
__publicField(this, "s");
|
|
212
|
+
__publicField(this, "i", 0);
|
|
213
|
+
this.s = [...text];
|
|
214
|
+
}
|
|
215
|
+
atEnd() {
|
|
216
|
+
this.skip();
|
|
217
|
+
return this.i === this.s.length;
|
|
218
|
+
}
|
|
219
|
+
skip() {
|
|
220
|
+
while (this.i < this.s.length && " \n\r".includes(this.s[this.i])) this.i++;
|
|
221
|
+
}
|
|
222
|
+
eat(token) {
|
|
223
|
+
this.skip();
|
|
224
|
+
const t = [...token];
|
|
225
|
+
if (this.i + t.length > this.s.length) return false;
|
|
226
|
+
for (let k = 0; k < t.length; k++) if (this.s[this.i + k] !== t[k]) return false;
|
|
227
|
+
if (t.length === 1 && this.s[this.i + 1] === "=" && "<>!=".includes(t[0])) return false;
|
|
228
|
+
this.i += t.length;
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
or() {
|
|
232
|
+
let l = this.and();
|
|
233
|
+
if (!l) return null;
|
|
234
|
+
while (this.eat("||")) {
|
|
235
|
+
const r = this.and();
|
|
236
|
+
if (!r) return null;
|
|
237
|
+
l = { k: "bin", op: "||", l, r };
|
|
238
|
+
}
|
|
239
|
+
return l;
|
|
240
|
+
}
|
|
241
|
+
and() {
|
|
242
|
+
let l = this.compare();
|
|
243
|
+
if (!l) return null;
|
|
244
|
+
while (this.eat("&&")) {
|
|
245
|
+
const r = this.compare();
|
|
246
|
+
if (!r) return null;
|
|
247
|
+
l = { k: "bin", op: "&&", l, r };
|
|
248
|
+
}
|
|
249
|
+
return l;
|
|
250
|
+
}
|
|
251
|
+
compare() {
|
|
252
|
+
const l = this.sum();
|
|
253
|
+
if (!l) return null;
|
|
254
|
+
for (const op of ["<=", ">=", "==", "!=", "<", ">"]) {
|
|
255
|
+
if (this.eat(op)) {
|
|
256
|
+
const r = this.sum();
|
|
257
|
+
return r ? { k: "bin", op, l, r } : null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return l;
|
|
261
|
+
}
|
|
262
|
+
sum() {
|
|
263
|
+
let l = this.product();
|
|
264
|
+
if (!l) return null;
|
|
265
|
+
for (; ; ) {
|
|
266
|
+
const op = this.eat("+") ? "+" : this.eat("-") ? "-" : null;
|
|
267
|
+
if (!op) return l;
|
|
268
|
+
const r = this.product();
|
|
269
|
+
if (!r) return null;
|
|
270
|
+
l = { k: "bin", op, l, r };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
product() {
|
|
274
|
+
let l = this.unary();
|
|
275
|
+
if (!l) return null;
|
|
276
|
+
for (; ; ) {
|
|
277
|
+
const op = ["*", "/", "%"].find((o) => this.eat(o));
|
|
278
|
+
if (!op) return l;
|
|
279
|
+
const r = this.unary();
|
|
280
|
+
if (!r) return null;
|
|
281
|
+
l = { k: "bin", op, l, r };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
unary() {
|
|
285
|
+
if (this.eat("!")) {
|
|
286
|
+
const e = this.unary();
|
|
287
|
+
return e ? { k: "not", e } : null;
|
|
288
|
+
}
|
|
289
|
+
if (this.eat("-")) {
|
|
290
|
+
const e = this.unary();
|
|
291
|
+
return e ? { k: "neg", e } : null;
|
|
292
|
+
}
|
|
293
|
+
return this.primary();
|
|
294
|
+
}
|
|
295
|
+
primary() {
|
|
296
|
+
this.skip();
|
|
297
|
+
if (this.i >= this.s.length) return null;
|
|
298
|
+
if (this.eat("(")) {
|
|
299
|
+
const e = this.or();
|
|
300
|
+
return e && this.eat(")") ? e : null;
|
|
301
|
+
}
|
|
302
|
+
const s = this.s;
|
|
303
|
+
const c = s[this.i];
|
|
304
|
+
if (isDigit(c)) {
|
|
305
|
+
const start3 = this.i;
|
|
306
|
+
while (isDigit(s[this.i])) this.i++;
|
|
307
|
+
if (s[this.i] === "." && isDigit(s[this.i + 1])) {
|
|
308
|
+
this.i++;
|
|
309
|
+
while (isDigit(s[this.i])) this.i++;
|
|
310
|
+
}
|
|
311
|
+
return { k: "lit", v: Number(s.slice(start3, this.i).join("")) };
|
|
312
|
+
}
|
|
313
|
+
if (!isLetter(c)) return null;
|
|
314
|
+
const start2 = this.i;
|
|
315
|
+
while (isLetter(s[this.i]) || isDigit(s[this.i])) this.i++;
|
|
316
|
+
const name = s.slice(start2, this.i).join("");
|
|
317
|
+
if (name.length > MAX_NAME) return null;
|
|
318
|
+
let j = this.i;
|
|
319
|
+
while (s[j] === " " || s[j] === " ") j++;
|
|
320
|
+
const called = s[j] === "(";
|
|
321
|
+
if (METRIC_NAMES.includes(name)) {
|
|
322
|
+
if (!called) return { k: "metric", metric: name, target: "" };
|
|
323
|
+
this.eat("(");
|
|
324
|
+
const from = this.i;
|
|
325
|
+
while (this.i < s.length && s[this.i] !== ")" && s[this.i] !== "(") this.i++;
|
|
326
|
+
if (s[this.i] !== ")") return null;
|
|
327
|
+
const target = s.slice(from, this.i).join("").replace(/^[ \t]+|[ \t]+$/g, "");
|
|
328
|
+
this.i++;
|
|
329
|
+
return { k: "metric", metric: name, target };
|
|
330
|
+
}
|
|
331
|
+
if (name in FUNCTIONS) {
|
|
332
|
+
if (!called || !this.eat("(")) return null;
|
|
333
|
+
const args = [];
|
|
334
|
+
if (!this.eat(")")) {
|
|
335
|
+
do {
|
|
336
|
+
const a = this.or();
|
|
337
|
+
if (!a) return null;
|
|
338
|
+
args.push(a);
|
|
339
|
+
} while (this.eat(","));
|
|
340
|
+
if (!this.eat(")")) return null;
|
|
341
|
+
}
|
|
342
|
+
const arity = FUNCTIONS[name];
|
|
343
|
+
if (arity !== null ? args.length !== arity : args.length === 0) return null;
|
|
344
|
+
return { k: "call", fn: name, args };
|
|
345
|
+
}
|
|
346
|
+
if (called) return null;
|
|
347
|
+
if (name === "true") return { k: "lit", v: true };
|
|
348
|
+
if (name === "false") return { k: "lit", v: false };
|
|
349
|
+
return { k: "name", name };
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
function describeVar(v) {
|
|
353
|
+
return typeof v === "boolean" ? v ? "true" : "false" : formatTwo(v);
|
|
354
|
+
}
|
|
355
|
+
function formatTwo(v) {
|
|
356
|
+
const n = Math.floor(Math.abs(v) * 100 + 0.5);
|
|
357
|
+
if (!(n < 1e15)) return v < 0 ? "-\u221E" : "\u221E";
|
|
358
|
+
const whole = Math.floor(n / 100), part = n - whole * 100;
|
|
359
|
+
let s = String(whole);
|
|
360
|
+
if (part > 0) s += "." + String(part).padStart(2, "0").replace(/0+$/, "");
|
|
361
|
+
return (v < 0 && n > 0 ? "-" : "") + s;
|
|
362
|
+
}
|
|
363
|
+
function filling(text, values) {
|
|
364
|
+
if (!text.includes("{")) return text;
|
|
365
|
+
let out = "", rest = text;
|
|
366
|
+
for (let open = rest.indexOf("{"); open >= 0; open = rest.indexOf("{")) {
|
|
367
|
+
out += rest.slice(0, open);
|
|
368
|
+
const close = rest.indexOf("}", open);
|
|
369
|
+
if (close < 0) {
|
|
370
|
+
out += rest.slice(open);
|
|
371
|
+
rest = "";
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
const inner = rest.slice(open + 1, close);
|
|
375
|
+
const e = inner.includes("{") ? null : parseExpr(inner);
|
|
376
|
+
const names = e ? exprNames(e) : [];
|
|
377
|
+
if (e && exprMetrics(e).length === 0 && names.length > 0 && names.every((n) => values[n] !== void 0)) out += describeVar(evaluate(e, values));
|
|
378
|
+
else out += rest.slice(open, close + 1);
|
|
379
|
+
rest = rest.slice(close + 1);
|
|
380
|
+
}
|
|
381
|
+
return out + rest;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// src/state.ts
|
|
385
|
+
var MAX_NAME2 = 64;
|
|
386
|
+
function isName(name) {
|
|
387
|
+
return name.length <= MAX_NAME2 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
388
|
+
}
|
|
389
|
+
function mentionsMetric(condition) {
|
|
390
|
+
const e = parseExpr(condition);
|
|
391
|
+
return e !== null && exprMetrics(e).length > 0;
|
|
392
|
+
}
|
|
129
393
|
function holds(condition, state, metric = null) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (m !== null) {
|
|
134
|
-
const v = metric ? metric(m) : null;
|
|
135
|
-
return v !== null && passes(m, v);
|
|
136
|
-
}
|
|
137
|
-
let term = trim(all);
|
|
138
|
-
let negated = false;
|
|
139
|
-
if (term.startsWith("!")) {
|
|
140
|
-
negated = true;
|
|
141
|
-
term = trim(term.slice(1));
|
|
142
|
-
}
|
|
143
|
-
return (state[term] ?? false) !== negated;
|
|
144
|
-
}));
|
|
394
|
+
const e = parseExpr(condition);
|
|
395
|
+
if (!e) return false;
|
|
396
|
+
return truthy(evaluate(e, state, metric ? (m, target) => metric({ metric: m, target, op: "", value: 0 }) : null));
|
|
145
397
|
}
|
|
146
398
|
function stateValues(page, choice) {
|
|
147
399
|
if (choice === "all") return null;
|
|
@@ -180,7 +432,7 @@ function applyOverride(o, e) {
|
|
|
180
432
|
return out;
|
|
181
433
|
}
|
|
182
434
|
function changesState(c) {
|
|
183
|
-
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0);
|
|
435
|
+
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0 || !!c.run);
|
|
184
436
|
}
|
|
185
437
|
function applyClick(c, state, initial = {}) {
|
|
186
438
|
let out = { ...state };
|
|
@@ -189,27 +441,91 @@ function applyClick(c, state, initial = {}) {
|
|
|
189
441
|
for (const k of Object.keys(state)) out[k] = false;
|
|
190
442
|
Object.assign(out, initial);
|
|
191
443
|
}
|
|
192
|
-
|
|
193
|
-
for (const k of c.
|
|
444
|
+
const before = { ...out };
|
|
445
|
+
for (const [k, v] of Object.entries(c.set ?? {})) out[k] = clickValue(v, before, before[k]);
|
|
446
|
+
for (const k of c.toggle ?? []) out[k] = !truthy(out[k] ?? false);
|
|
194
447
|
return out;
|
|
195
448
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
449
|
+
function clickValue(v, state, current) {
|
|
450
|
+
if (typeof v !== "string") return v;
|
|
451
|
+
const e = parseExpr(v);
|
|
452
|
+
return e ? evaluate(e, state) : current ?? false;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/theme.ts
|
|
456
|
+
var rgb = (r, g, b) => ({ r, g, b, a: 1 });
|
|
457
|
+
function hex(h) {
|
|
458
|
+
const n = parseInt(h.slice(1), 16);
|
|
459
|
+
return { r: (n >> 16 & 255) / 255, g: (n >> 8 & 255) / 255, b: (n & 255) / 255, a: 1 };
|
|
460
|
+
}
|
|
461
|
+
var PALETTE_NAMES = ["accent", "muted", "danger", "warning", "success", "info"];
|
|
462
|
+
var palette = (colors) => PALETTE_NAMES.map((name, i) => ({ name, color: hex(colors[i]) }));
|
|
463
|
+
var LIGHT_PALETTE = palette(["#2563eb", "#6b7280", "#dc2626", "#d97706", "#16a34a", "#0891b2"]);
|
|
464
|
+
var DARK_PALETTE = palette(["#93c5fd", "#cbd5e1", "#fca5a5", "#fcd34d", "#86efac", "#67e8f9"]);
|
|
465
|
+
var themes = [
|
|
466
|
+
{ id: "paper", name: "Paper", background: rgb(0.984, 0.98, 0.965), ink: rgb(0.12, 0.12, 0.13), palette: LIGHT_PALETTE },
|
|
467
|
+
{ id: "white", name: "White", background: rgb(1, 1, 1), ink: rgb(0.12, 0.12, 0.13), palette: LIGHT_PALETTE },
|
|
468
|
+
{
|
|
469
|
+
id: "sepia",
|
|
470
|
+
name: "Sepia",
|
|
471
|
+
background: rgb(0.953, 0.914, 0.824),
|
|
472
|
+
ink: rgb(0.23, 0.18, 0.13),
|
|
473
|
+
palette: palette(["#9a3412", "#78716c", "#b91c1c", "#b45309", "#4d7c0f", "#0e7490"])
|
|
474
|
+
},
|
|
475
|
+
{ id: "blueprint", name: "Blueprint", background: rgb(0.106, 0.227, 0.361), ink: rgb(0.91, 0.945, 0.984), palette: DARK_PALETTE },
|
|
476
|
+
{ id: "chalkboard", name: "Chalkboard", background: rgb(0.169, 0.227, 0.2), ink: rgb(0.945, 0.945, 0.918), palette: DARK_PALETTE },
|
|
477
|
+
{ id: "midnight", name: "Midnight", background: rgb(0.082, 0.086, 0.102), ink: rgb(0.925, 0.925, 0.94), palette: DARK_PALETTE }
|
|
478
|
+
];
|
|
479
|
+
function themeNamed(id) {
|
|
480
|
+
return themes.find((t) => t.id === id) ?? null;
|
|
481
|
+
}
|
|
482
|
+
function pageTheme(page) {
|
|
483
|
+
const c = page.customTheme;
|
|
484
|
+
if (c) return { id: "custom", name: c.name, background: c.background, ink: c.ink, palette: c.palette };
|
|
485
|
+
return themeNamed(page.theme);
|
|
486
|
+
}
|
|
487
|
+
function paletteColor(page, name) {
|
|
488
|
+
const n = name.trim().toLowerCase();
|
|
489
|
+
return (pageTheme(page)?.palette ?? LIGHT_PALETTE).find((p) => p.name.toLowerCase() === n)?.color ?? null;
|
|
490
|
+
}
|
|
491
|
+
function resolvingPalette(page) {
|
|
492
|
+
const named = (e) => !!e.style.named;
|
|
493
|
+
if (!page.elements.some(named) && !(page.symbols ?? []).some((s) => s.elements.some(named))) return page;
|
|
494
|
+
const resolve2 = (e) => {
|
|
495
|
+
const n = e.style.named;
|
|
496
|
+
if (!n) return e;
|
|
497
|
+
const style2 = { ...e.style };
|
|
498
|
+
const f = n.fill !== null ? paletteColor(page, n.fill) : null;
|
|
499
|
+
const s = n.stroke !== null ? paletteColor(page, n.stroke) : null;
|
|
500
|
+
const t = n.textColor !== null ? paletteColor(page, n.textColor) : null;
|
|
501
|
+
if (f) style2.fill = { ...f };
|
|
502
|
+
if (s) style2.stroke = { ...s };
|
|
503
|
+
if (t) style2.textColor = { ...t };
|
|
504
|
+
return { ...e, style: style2 };
|
|
505
|
+
};
|
|
506
|
+
return {
|
|
507
|
+
...page,
|
|
508
|
+
elements: page.elements.map(resolve2),
|
|
509
|
+
symbols: page.symbols ? page.symbols.map((s) => ({ ...s, elements: s.elements.map(resolve2) })) : page.symbols
|
|
510
|
+
};
|
|
198
511
|
}
|
|
199
512
|
|
|
200
513
|
// src/model.ts
|
|
201
|
-
var SUPPORTED_VERSION =
|
|
514
|
+
var SUPPORTED_VERSION = 3;
|
|
202
515
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
203
516
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
204
517
|
var KNOWN_FIELDS = {
|
|
205
518
|
file: ["version", "pages", "images"],
|
|
206
|
-
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
519
|
+
page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
207
520
|
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
208
521
|
flowOverride: ["when", "volume"],
|
|
209
522
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
210
523
|
layer: ["id", "name", "visible", "locked"],
|
|
211
524
|
rgba: ["r", "g", "b", "a"],
|
|
212
|
-
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor"],
|
|
525
|
+
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor", "named"],
|
|
526
|
+
colorNames: ["fill", "stroke", "textColor"],
|
|
527
|
+
customTheme: ["name", "background", "ink", "palette", "base"],
|
|
528
|
+
paletteColor: ["name", "color"],
|
|
213
529
|
element: [
|
|
214
530
|
"id",
|
|
215
531
|
"kind",
|
|
@@ -247,8 +563,11 @@ var KNOWN_FIELDS = {
|
|
|
247
563
|
"capacity",
|
|
248
564
|
"delay",
|
|
249
565
|
"queue",
|
|
250
|
-
"gauge"
|
|
566
|
+
"gauge",
|
|
567
|
+
"containerID",
|
|
568
|
+
"container"
|
|
251
569
|
],
|
|
570
|
+
container: ["fixed", "lanes"],
|
|
252
571
|
gauge: ["metric", "of", "max", "warn", "alarm", "style", "label"],
|
|
253
572
|
strokePoint: ["x", "y"],
|
|
254
573
|
subpath: ["closed", "coords"],
|
|
@@ -257,7 +576,7 @@ var KNOWN_FIELDS = {
|
|
|
257
576
|
extensionRef: ["id", "shape", "version", "params"],
|
|
258
577
|
normalizedPoint: ["x", "y"],
|
|
259
578
|
normalizedRect: ["x", "y", "width", "height"],
|
|
260
|
-
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops", "cost"],
|
|
579
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops", "avoid", "cost"],
|
|
261
580
|
waypoint: ["x", "y"],
|
|
262
581
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
263
582
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
@@ -266,7 +585,7 @@ var KNOWN_FIELDS = {
|
|
|
266
585
|
repeat: ["start", "end", "count"],
|
|
267
586
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
268
587
|
marker: ["t", "name", "loop"],
|
|
269
|
-
click: ["scene", "url", "set", "toggle", "reset"],
|
|
588
|
+
click: ["scene", "url", "set", "toggle", "reset", "run"],
|
|
270
589
|
hover: ["stroke", "fill", "opacity"],
|
|
271
590
|
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
272
591
|
};
|
|
@@ -280,7 +599,7 @@ var inkOnDark = { r: 0.93, g: 0.93, b: 0.94, a: 1 };
|
|
|
280
599
|
var DottieFormatError = class extends Error {
|
|
281
600
|
};
|
|
282
601
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
283
|
-
var
|
|
602
|
+
var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
284
603
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
285
604
|
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
286
605
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
@@ -304,9 +623,9 @@ function webLink(url) {
|
|
|
304
623
|
}
|
|
305
624
|
function rgba(v) {
|
|
306
625
|
if (!isObj(v)) return void 0;
|
|
307
|
-
const r =
|
|
626
|
+
const r = num2(v.r), g = num2(v.g), b = num2(v.b);
|
|
308
627
|
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
309
|
-
return { r, g, b, a:
|
|
628
|
+
return { r, g, b, a: num2(v.a) ?? 1 };
|
|
310
629
|
}
|
|
311
630
|
var Reader = class {
|
|
312
631
|
constructor() {
|
|
@@ -335,28 +654,55 @@ var Reader = class {
|
|
|
335
654
|
stroke: rgba(s.stroke) ?? { ...black },
|
|
336
655
|
fill: rgba(s.fill) ?? { ...clear },
|
|
337
656
|
fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
|
|
338
|
-
strokeWidth:
|
|
657
|
+
strokeWidth: num2(s.strokeWidth) ?? 2,
|
|
339
658
|
dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
|
|
340
|
-
roughness:
|
|
341
|
-
bowing:
|
|
342
|
-
fontSize:
|
|
659
|
+
roughness: num2(s.roughness) ?? 1,
|
|
660
|
+
bowing: num2(s.bowing) ?? 1,
|
|
661
|
+
fontSize: num2(s.fontSize) ?? 18,
|
|
343
662
|
font: str(s.font) || null,
|
|
344
|
-
gridSize:
|
|
345
|
-
opacity:
|
|
346
|
-
dashOffset:
|
|
347
|
-
textColor: rgba(s.textColor) ?? null
|
|
663
|
+
gridSize: num2(s.gridSize) ?? null,
|
|
664
|
+
opacity: num2(s.opacity) ?? null,
|
|
665
|
+
dashOffset: num2(s.dashOffset) ?? null,
|
|
666
|
+
textColor: rgba(s.textColor) ?? null,
|
|
667
|
+
named: this.colorNames(s.named)
|
|
348
668
|
};
|
|
349
669
|
}
|
|
670
|
+
container(v) {
|
|
671
|
+
if (!isObj(v)) return null;
|
|
672
|
+
this.fields(v, "container");
|
|
673
|
+
return { fixed: v.fixed === true ? true : null, lanes: v.lanes === "rows" || v.lanes === "columns" ? v.lanes : null };
|
|
674
|
+
}
|
|
675
|
+
colorNames(v) {
|
|
676
|
+
if (!isObj(v)) return null;
|
|
677
|
+
this.fields(v, "colorNames");
|
|
678
|
+
const n = { fill: str(v.fill) ?? null, stroke: str(v.stroke) ?? null, textColor: str(v.textColor) ?? null };
|
|
679
|
+
return n.fill === null && n.stroke === null && n.textColor === null ? null : n;
|
|
680
|
+
}
|
|
681
|
+
customTheme(v) {
|
|
682
|
+
if (!isObj(v)) return null;
|
|
683
|
+
this.fields(v, "customTheme");
|
|
684
|
+
const background = rgba(v.background), ink = rgba(v.ink);
|
|
685
|
+
if (!background || !ink) {
|
|
686
|
+
this.warn("A custom theme needs a background and an ink; drawn without it");
|
|
687
|
+
return null;
|
|
688
|
+
}
|
|
689
|
+
const palette2 = (Array.isArray(v.palette) ? v.palette : []).filter(isObj).flatMap((p) => {
|
|
690
|
+
this.fields(p, "paletteColor");
|
|
691
|
+
const name = str(p.name), color2 = rgba(p.color);
|
|
692
|
+
return name && color2 ? [{ name, color: color2 }] : [];
|
|
693
|
+
});
|
|
694
|
+
return { name: str(v.name) ?? "Custom", background, ink, palette: palette2, base: str(v.base) ?? null };
|
|
695
|
+
}
|
|
350
696
|
endpoint(v) {
|
|
351
697
|
const e = isObj(v) ? v : {};
|
|
352
698
|
const port = str(e.port);
|
|
353
|
-
const portIndex =
|
|
699
|
+
const portIndex = num2(e.portIndex);
|
|
354
700
|
return {
|
|
355
701
|
elementID: uuid(e.elementID) ?? null,
|
|
356
702
|
port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
|
|
357
703
|
portIndex: portIndex !== void 0 && Number.isInteger(portIndex) && portIndex >= 0 ? portIndex : null,
|
|
358
|
-
x:
|
|
359
|
-
y:
|
|
704
|
+
x: num2(e.x) ?? 0,
|
|
705
|
+
y: num2(e.y) ?? 0
|
|
360
706
|
};
|
|
361
707
|
}
|
|
362
708
|
connector(v) {
|
|
@@ -375,9 +721,10 @@ var Reader = class {
|
|
|
375
721
|
endHead: head(v.endHead, "arrow"),
|
|
376
722
|
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
377
723
|
this.fields(w, "waypoint");
|
|
378
|
-
return { x:
|
|
724
|
+
return { x: num2(w.x) ?? 0, y: num2(w.y) ?? 0 };
|
|
379
725
|
}) : null,
|
|
380
726
|
hops: typeof v.hops === "boolean" ? v.hops : null,
|
|
727
|
+
avoid: typeof v.avoid === "boolean" ? v.avoid : null,
|
|
381
728
|
cost: typeof v.cost === "number" && Number.isFinite(v.cost) ? v.cost : null
|
|
382
729
|
};
|
|
383
730
|
}
|
|
@@ -410,20 +757,20 @@ var Reader = class {
|
|
|
410
757
|
id,
|
|
411
758
|
kind,
|
|
412
759
|
layerID,
|
|
413
|
-
x:
|
|
414
|
-
y:
|
|
415
|
-
width:
|
|
416
|
-
height:
|
|
760
|
+
x: num2(v.x) ?? 0,
|
|
761
|
+
y: num2(v.y) ?? 0,
|
|
762
|
+
width: num2(v.width) ?? 0,
|
|
763
|
+
height: num2(v.height) ?? 0,
|
|
417
764
|
style: this.style(v.style),
|
|
418
|
-
seed: (
|
|
765
|
+
seed: (num2(v.seed) ?? 1) >>> 0,
|
|
419
766
|
text: str(v.text) ?? null,
|
|
420
767
|
connector: this.connector(v.connector),
|
|
421
768
|
groupID: uuid(v.groupID) ?? null,
|
|
422
|
-
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x:
|
|
769
|
+
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num2(p.x) ?? 0, y: num2(p.y) ?? 0 })) : null,
|
|
423
770
|
path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
|
|
424
771
|
subpaths,
|
|
425
772
|
evenOdd: bool(v.evenOdd) ?? null,
|
|
426
|
-
rotation:
|
|
773
|
+
rotation: num2(v.rotation) ?? null,
|
|
427
774
|
link: uuid(v.link) ?? null,
|
|
428
775
|
url: str(v.url) ?? null,
|
|
429
776
|
anchor: str(v.anchor) ?? null,
|
|
@@ -433,20 +780,22 @@ var Reader = class {
|
|
|
433
780
|
overrides: this.overrides(v.overrides),
|
|
434
781
|
symbol: uuid(v.symbol) ?? null,
|
|
435
782
|
playback: this.playback(v.playback),
|
|
436
|
-
offset:
|
|
783
|
+
offset: num2(v.offset) ?? null,
|
|
437
784
|
args: strings(v.args),
|
|
438
785
|
image: this.image(v.image, images),
|
|
439
|
-
reveal:
|
|
786
|
+
reveal: num2(v.reveal) ?? null,
|
|
440
787
|
ports: Array.isArray(v.ports) ? v.ports.map((p) => {
|
|
441
788
|
const o = isObj(p) ? p : {};
|
|
442
|
-
return { x:
|
|
789
|
+
return { x: num2(o.x) ?? 0, y: num2(o.y) ?? 0 };
|
|
443
790
|
}) : null,
|
|
444
791
|
labelRect: this.labelRect(v.labelRect),
|
|
445
792
|
extension: this.extension(v.extension),
|
|
446
|
-
capacity:
|
|
447
|
-
delay:
|
|
793
|
+
capacity: num2(v.capacity) ?? null,
|
|
794
|
+
delay: num2(v.delay) ?? null,
|
|
448
795
|
queue: v.queue === true ? true : null,
|
|
449
|
-
gauge: this.gauge(v.gauge)
|
|
796
|
+
gauge: this.gauge(v.gauge),
|
|
797
|
+
containerID: uuid(v.containerID) ?? null,
|
|
798
|
+
container: this.container(v.container)
|
|
450
799
|
};
|
|
451
800
|
}
|
|
452
801
|
gauge(v) {
|
|
@@ -467,7 +816,7 @@ var Reader = class {
|
|
|
467
816
|
labelRect(v) {
|
|
468
817
|
if (!isObj(v)) return null;
|
|
469
818
|
this.fields(v, "normalizedRect");
|
|
470
|
-
return { x:
|
|
819
|
+
return { x: num2(v.x) ?? 0, y: num2(v.y) ?? 0, width: num2(v.width) ?? 0, height: num2(v.height) ?? 0 };
|
|
471
820
|
}
|
|
472
821
|
extension(v) {
|
|
473
822
|
if (!isObj(v)) return null;
|
|
@@ -488,7 +837,7 @@ var Reader = class {
|
|
|
488
837
|
variables(v) {
|
|
489
838
|
if (!isObj(v)) return null;
|
|
490
839
|
const out = {};
|
|
491
|
-
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
840
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
|
|
492
841
|
return Object.keys(out).length > 0 ? out : null;
|
|
493
842
|
}
|
|
494
843
|
click(v) {
|
|
@@ -500,12 +849,22 @@ var Reader = class {
|
|
|
500
849
|
let set = null;
|
|
501
850
|
if (isObj(v.set)) {
|
|
502
851
|
set = {};
|
|
503
|
-
for (const [k, b] of Object.entries(v.set))
|
|
852
|
+
for (const [k, b] of Object.entries(v.set)) {
|
|
853
|
+
if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
|
|
854
|
+
}
|
|
504
855
|
if (Object.keys(set).length === 0) set = null;
|
|
505
856
|
}
|
|
506
857
|
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
507
858
|
const toggle = raw ? raw.filter(isName) : null;
|
|
508
|
-
|
|
859
|
+
const run = str(v.run);
|
|
860
|
+
return {
|
|
861
|
+
scene: str(v.scene) ?? null,
|
|
862
|
+
url: safe,
|
|
863
|
+
set,
|
|
864
|
+
toggle: toggle && toggle.length > 0 ? toggle : null,
|
|
865
|
+
reset: v.reset === true ? true : null,
|
|
866
|
+
run: run !== void 0 && isName(run) ? run : null
|
|
867
|
+
};
|
|
509
868
|
}
|
|
510
869
|
playback(v) {
|
|
511
870
|
const s = str(v);
|
|
@@ -521,7 +880,7 @@ var Reader = class {
|
|
|
521
880
|
return null;
|
|
522
881
|
}
|
|
523
882
|
const size = (x) => {
|
|
524
|
-
const n =
|
|
883
|
+
const n = num2(x);
|
|
525
884
|
return Math.max(1, n ?? 100);
|
|
526
885
|
};
|
|
527
886
|
const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
|
|
@@ -558,8 +917,8 @@ var Reader = class {
|
|
|
558
917
|
stroke: rgba(o.stroke) ?? null,
|
|
559
918
|
fill: rgba(o.fill) ?? null,
|
|
560
919
|
textColor: rgba(o.textColor) ?? null,
|
|
561
|
-
opacity:
|
|
562
|
-
strokeWidth:
|
|
920
|
+
opacity: num2(o.opacity) ?? null,
|
|
921
|
+
strokeWidth: num2(o.strokeWidth) ?? null,
|
|
563
922
|
dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
|
|
564
923
|
text: str(o.text) ?? null,
|
|
565
924
|
reroute,
|
|
@@ -573,7 +932,7 @@ var Reader = class {
|
|
|
573
932
|
hover(v) {
|
|
574
933
|
if (!isObj(v)) return null;
|
|
575
934
|
this.fields(v, "hover");
|
|
576
|
-
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity:
|
|
935
|
+
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num2(v.opacity) ?? null };
|
|
577
936
|
}
|
|
578
937
|
ease(v) {
|
|
579
938
|
const s = str(v);
|
|
@@ -587,11 +946,11 @@ var Reader = class {
|
|
|
587
946
|
keyframe(v) {
|
|
588
947
|
if (!isObj(v)) return null;
|
|
589
948
|
this.fields(v, "keyframe");
|
|
590
|
-
const t =
|
|
949
|
+
const t = num2(v.t);
|
|
591
950
|
if (t === void 0) return null;
|
|
592
951
|
const k = { t };
|
|
593
952
|
for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
|
|
594
|
-
const n =
|
|
953
|
+
const n = num2(v[p]);
|
|
595
954
|
if (n !== void 0) k[p] = n;
|
|
596
955
|
}
|
|
597
956
|
const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
|
|
@@ -609,39 +968,39 @@ var Reader = class {
|
|
|
609
968
|
const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
|
|
610
969
|
stableSortBy(keys, (k) => k.t);
|
|
611
970
|
const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
|
|
612
|
-
const path = uuid(f.path), start2 =
|
|
971
|
+
const path = uuid(f.path), start2 = num2(f.start), end = num2(f.end);
|
|
613
972
|
if (!path || start2 === void 0 || end === void 0 || start2 < 0 || end > MAX_DURATION) return [];
|
|
614
|
-
return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps:
|
|
973
|
+
return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps: num2(f.laps) ?? null }];
|
|
615
974
|
}) : null;
|
|
616
975
|
const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
|
|
617
|
-
const start2 =
|
|
976
|
+
const start2 = num2(r.start), end = num2(r.end);
|
|
618
977
|
if (start2 === void 0 || end === void 0) return [];
|
|
619
|
-
const count =
|
|
978
|
+
const count = num2(r.count);
|
|
620
979
|
const rep = { start: start2, end, count: count === void 0 ? null : Math.trunc(count) };
|
|
621
980
|
return repeatIsValid(rep) ? [rep] : [];
|
|
622
981
|
}) : null;
|
|
623
|
-
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(
|
|
982
|
+
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(num2(v.march)) };
|
|
624
983
|
}
|
|
625
984
|
timeline(v) {
|
|
626
985
|
if (!isObj(v)) return null;
|
|
627
986
|
this.fields(v, "timeline");
|
|
628
|
-
const boil =
|
|
987
|
+
const boil = num2(v.boil);
|
|
629
988
|
const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
|
|
630
|
-
const t =
|
|
989
|
+
const t = num2(c.t), x = num2(c.x), y = num2(c.y), width = num2(c.width), height = num2(c.height);
|
|
631
990
|
if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
|
|
632
991
|
if (!(width > 1 && height > 1 && t >= 0)) return [];
|
|
633
992
|
return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
|
|
634
993
|
}) : null;
|
|
635
994
|
if (camera) stableSortBy(camera, (c) => c.t);
|
|
636
995
|
const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
|
|
637
|
-
const t =
|
|
996
|
+
const t = num2(m.t);
|
|
638
997
|
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
|
|
639
998
|
}) : null;
|
|
640
999
|
if (markers) stableSortBy(markers, (m) => m.t);
|
|
641
|
-
const poster =
|
|
1000
|
+
const poster = num2(v.poster);
|
|
642
1001
|
return {
|
|
643
|
-
fps: clampFPS(
|
|
644
|
-
duration: clampTime(
|
|
1002
|
+
fps: clampFPS(num2(v.fps) ?? 12),
|
|
1003
|
+
duration: clampTime(num2(v.duration) ?? 0),
|
|
645
1004
|
boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
|
|
646
1005
|
settle: bool(v.settle) ?? null,
|
|
647
1006
|
poster: poster === void 0 ? null : clampTime(poster),
|
|
@@ -667,6 +1026,8 @@ var Reader = class {
|
|
|
667
1026
|
layers,
|
|
668
1027
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
669
1028
|
theme: str(v.theme) ?? null,
|
|
1029
|
+
customTheme: this.customTheme(v.customTheme),
|
|
1030
|
+
logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
|
|
670
1031
|
timeline: this.timeline(v.timeline),
|
|
671
1032
|
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
672
1033
|
variables: this.variables(v.variables),
|
|
@@ -678,7 +1039,7 @@ var Reader = class {
|
|
|
678
1039
|
this.fields(f, "flow");
|
|
679
1040
|
const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
|
|
680
1041
|
const finite = (x) => {
|
|
681
|
-
const n =
|
|
1042
|
+
const n = num2(x);
|
|
682
1043
|
return n !== void 0 && Number.isFinite(n) ? n : null;
|
|
683
1044
|
};
|
|
684
1045
|
return {
|
|
@@ -718,7 +1079,7 @@ function parseDottie(source) {
|
|
|
718
1079
|
}
|
|
719
1080
|
}
|
|
720
1081
|
if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
|
|
721
|
-
const version =
|
|
1082
|
+
const version = num2(json.version);
|
|
722
1083
|
if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
|
|
723
1084
|
if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
|
|
724
1085
|
const reader = new Reader();
|
|
@@ -730,7 +1091,7 @@ function parseDottie(source) {
|
|
|
730
1091
|
if (isObj(v) && typeof v.data === "string") images[k] = reader.picture(v.data, str(v.format));
|
|
731
1092
|
}
|
|
732
1093
|
}
|
|
733
|
-
const pages = json.pages.map((p, i) => reader.page(p, images, i + 1));
|
|
1094
|
+
const pages = json.pages.map((p, i) => resolvingPalette(reader.page(p, images, i + 1)));
|
|
734
1095
|
if (new Set(pages.map((p) => p.id)).size !== pages.length) throw new DottieFormatError("Two pages share an id");
|
|
735
1096
|
return { version, pages, warnings: reader.warnings };
|
|
736
1097
|
}
|
|
@@ -750,7 +1111,8 @@ function cloneElement(e) {
|
|
|
750
1111
|
...e.style,
|
|
751
1112
|
stroke: { ...e.style.stroke },
|
|
752
1113
|
fill: { ...e.style.fill },
|
|
753
|
-
textColor: e.style.textColor ? { ...e.style.textColor } : null
|
|
1114
|
+
textColor: e.style.textColor ? { ...e.style.textColor } : null,
|
|
1115
|
+
named: e.style.named ? { ...e.style.named } : e.style.named
|
|
754
1116
|
},
|
|
755
1117
|
connector: e.connector ? {
|
|
756
1118
|
...e.connector,
|
|
@@ -1240,6 +1602,165 @@ function shapePath(e) {
|
|
|
1240
1602
|
return path;
|
|
1241
1603
|
}
|
|
1242
1604
|
|
|
1605
|
+
// src/elbow.ts
|
|
1606
|
+
var MARGIN = 16;
|
|
1607
|
+
var BEND = 24;
|
|
1608
|
+
var SMALLEST = 24;
|
|
1609
|
+
var MOST = 48;
|
|
1610
|
+
function obstacle(e) {
|
|
1611
|
+
if (e.kind === "connector" || e.kind === "text" || e.kind === "freehand") return null;
|
|
1612
|
+
if (e.kind === "path" && !e.extension) return null;
|
|
1613
|
+
if (e.style.opacity !== null && e.style.opacity <= 0) return null;
|
|
1614
|
+
const r = rotatedBounds(e);
|
|
1615
|
+
return r.width < SMALLEST || r.height < SMALLEST ? null : r;
|
|
1616
|
+
}
|
|
1617
|
+
function crosses(points, r) {
|
|
1618
|
+
const x1 = r.x + r.width, y1 = r.y + r.height;
|
|
1619
|
+
for (let k = 1; k < points.length; k++) {
|
|
1620
|
+
const p = points[k - 1], q = points[k];
|
|
1621
|
+
if (p.y === q.y) {
|
|
1622
|
+
if (r.y < p.y && p.y < y1 && Math.min(p.x, q.x) < x1 && Math.max(p.x, q.x) > r.x) return true;
|
|
1623
|
+
} else if (p.x === q.x) {
|
|
1624
|
+
if (r.x < p.x && p.x < x1 && Math.min(p.y, q.y) < y1 && Math.max(p.y, q.y) > r.y) return true;
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
return false;
|
|
1628
|
+
}
|
|
1629
|
+
var padded = (r) => ({ x: r.x - MARGIN, y: r.y - MARGIN, width: r.width + 2 * MARGIN, height: r.height + 2 * MARGIN });
|
|
1630
|
+
var inside = (p, r) => r.x < p.x && p.x < r.x + r.width && r.y < p.y && p.y < r.y + r.height;
|
|
1631
|
+
var memo = /* @__PURE__ */ new Map();
|
|
1632
|
+
function avoiding(plain, a, b, startVertical, endVertical, elements, except) {
|
|
1633
|
+
const usable = (r) => !inside(a, padded(r)) && !inside(b, padded(r));
|
|
1634
|
+
const all = [];
|
|
1635
|
+
let crossed = false;
|
|
1636
|
+
for (const e of elements) {
|
|
1637
|
+
if (except.has(e.id)) continue;
|
|
1638
|
+
const r = obstacle(e);
|
|
1639
|
+
if (!r || !usable(r)) continue;
|
|
1640
|
+
all.push(r);
|
|
1641
|
+
if (!crossed && crosses(plain, r)) crossed = true;
|
|
1642
|
+
}
|
|
1643
|
+
if (!crossed) return plain;
|
|
1644
|
+
const key = JSON.stringify([a.x, a.y, b.x, b.y, startVertical, endVertical, all.map((r) => [r.x, r.y, r.width, r.height])]);
|
|
1645
|
+
const hit = memo.get(key);
|
|
1646
|
+
if (hit) return hit;
|
|
1647
|
+
const out = route(plain, a, b, startVertical, endVertical, all);
|
|
1648
|
+
if (memo.size >= 512) memo.clear();
|
|
1649
|
+
memo.set(key, out);
|
|
1650
|
+
return out;
|
|
1651
|
+
}
|
|
1652
|
+
function route(plain, a, b, startVertical, endVertical, usable) {
|
|
1653
|
+
let chosen = usable.map((_, i) => i).filter((i) => crosses(plain, usable[i]));
|
|
1654
|
+
if (chosen.length === 0) return plain;
|
|
1655
|
+
for (let round = 0; round < 4; round++) {
|
|
1656
|
+
if (chosen.length > MOST) return plain;
|
|
1657
|
+
const path = grid(a, b, startVertical, endVertical, chosen.map((i) => padded(usable[i])));
|
|
1658
|
+
if (!path) return plain;
|
|
1659
|
+
const more = usable.map((_, i) => i).filter((i) => !chosen.includes(i) && crosses(path, usable[i]));
|
|
1660
|
+
if (more.length === 0) return path;
|
|
1661
|
+
chosen = [...chosen, ...more].sort((p, q) => p - q);
|
|
1662
|
+
}
|
|
1663
|
+
return plain;
|
|
1664
|
+
}
|
|
1665
|
+
function grid(a, b, startVertical, endVertical, boxes) {
|
|
1666
|
+
let xs = [a.x, b.x, (a.x + b.x) / 2], ys = [a.y, b.y, (a.y + b.y) / 2];
|
|
1667
|
+
for (const r of boxes) {
|
|
1668
|
+
xs.push(r.x, r.x + r.width);
|
|
1669
|
+
ys.push(r.y, r.y + r.height);
|
|
1670
|
+
}
|
|
1671
|
+
xs = [...new Set(xs)].sort((p, q) => p - q);
|
|
1672
|
+
ys = [...new Set(ys)].sort((p, q) => p - q);
|
|
1673
|
+
const nx = xs.length, ny = ys.length;
|
|
1674
|
+
const ia = xs.indexOf(a.x), ja = ys.indexOf(a.y), ib = xs.indexOf(b.x), jb = ys.indexOf(b.y);
|
|
1675
|
+
if (ia < 0 || ja < 0 || ib < 0 || jb < 0) return null;
|
|
1676
|
+
const at = new Map(xs.map((x, k) => [x, k])), bt = new Map(ys.map((y, k) => [y, k]));
|
|
1677
|
+
const inner = new Uint8Array(nx * ny), acrossBlocked = new Uint8Array(nx * ny), downBlocked = new Uint8Array(nx * ny);
|
|
1678
|
+
for (const r of boxes) {
|
|
1679
|
+
const i0 = at.get(r.x), i1 = at.get(r.x + r.width), j0 = bt.get(r.y), j1 = bt.get(r.y + r.height);
|
|
1680
|
+
if (i0 === void 0 || i1 === void 0 || j0 === void 0 || j1 === void 0) continue;
|
|
1681
|
+
for (let i = i0; i < i1; i++) for (let j = j0; j < j1; j++) {
|
|
1682
|
+
if (j > j0) acrossBlocked[i * ny + j] = 1;
|
|
1683
|
+
if (i > i0) downBlocked[i * ny + j] = 1;
|
|
1684
|
+
if (i > i0 && j > j0) inner[i * ny + j] = 1;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
const blocked = (i, j) => inner[i * ny + j] === 1;
|
|
1688
|
+
const across = (i0, i1, j) => acrossBlocked[Math.min(i0, i1) * ny + j] === 1;
|
|
1689
|
+
const down = (j0, j1, i) => downBlocked[i * ny + Math.min(j0, j1)] === 1;
|
|
1690
|
+
const count = nx * ny * 2;
|
|
1691
|
+
const dist = new Array(count).fill(Infinity), prev = new Array(count).fill(-1);
|
|
1692
|
+
const done = new Array(count).fill(false);
|
|
1693
|
+
const heap = new Frontier();
|
|
1694
|
+
const first = (ia * ny + ja) * 2 + (startVertical ? 1 : 0);
|
|
1695
|
+
dist[first] = 0;
|
|
1696
|
+
heap.push(0, first);
|
|
1697
|
+
const want = (ib * ny + jb) * 2 + (endVertical ? 1 : 0);
|
|
1698
|
+
for (let top = heap.pop(); top; top = heap.pop()) {
|
|
1699
|
+
const [cost, s] = top;
|
|
1700
|
+
if (done[s]) continue;
|
|
1701
|
+
done[s] = true;
|
|
1702
|
+
if (s === want) break;
|
|
1703
|
+
const node = Math.floor(s / 2), axis = s % 2, i = Math.floor(node / ny), j = node % ny;
|
|
1704
|
+
for (const [di, dj] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
|
1705
|
+
const ni = i + di, nj = j + dj;
|
|
1706
|
+
if (ni < 0 || ni >= nx || nj < 0 || nj >= ny || blocked(ni, nj)) continue;
|
|
1707
|
+
const turn = di !== 0 ? 0 : 1;
|
|
1708
|
+
if (di !== 0 ? across(i, ni, j) : down(j, nj, i)) continue;
|
|
1709
|
+
const length = Math.abs(xs[ni] - xs[i]) + Math.abs(ys[nj] - ys[j]);
|
|
1710
|
+
const c = cost + length + (turn !== axis ? BEND : 0);
|
|
1711
|
+
const t = (ni * ny + nj) * 2 + turn;
|
|
1712
|
+
if (c < dist[t]) {
|
|
1713
|
+
dist[t] = c;
|
|
1714
|
+
prev[t] = s;
|
|
1715
|
+
heap.push(c, t);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
if (!done[want]) return null;
|
|
1720
|
+
const points = [];
|
|
1721
|
+
for (let s = want; s >= 0; s = prev[s]) {
|
|
1722
|
+
const node = Math.floor(s / 2);
|
|
1723
|
+
points.push({ x: xs[Math.floor(node / ny)], y: ys[node % ny] });
|
|
1724
|
+
}
|
|
1725
|
+
return points.reverse();
|
|
1726
|
+
}
|
|
1727
|
+
var Frontier = class {
|
|
1728
|
+
constructor() {
|
|
1729
|
+
__publicField(this, "heap", []);
|
|
1730
|
+
__publicField(this, "next", 0);
|
|
1731
|
+
}
|
|
1732
|
+
less(i, j) {
|
|
1733
|
+
const a = this.heap[i], b = this.heap[j];
|
|
1734
|
+
return a[0] < b[0] || a[0] === b[0] && a[1] < b[1];
|
|
1735
|
+
}
|
|
1736
|
+
push(cost, state) {
|
|
1737
|
+
this.heap.push([cost, this.next++, state]);
|
|
1738
|
+
for (let i = this.heap.length - 1; i > 0; ) {
|
|
1739
|
+
const up = i - 1 >> 1;
|
|
1740
|
+
if (!this.less(i, up)) break;
|
|
1741
|
+
[this.heap[i], this.heap[up]] = [this.heap[up], this.heap[i]];
|
|
1742
|
+
i = up;
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
pop() {
|
|
1746
|
+
if (this.heap.length === 0) return null;
|
|
1747
|
+
const top = this.heap[0], last = this.heap.pop();
|
|
1748
|
+
if (this.heap.length > 0) {
|
|
1749
|
+
this.heap[0] = last;
|
|
1750
|
+
for (let i = 0; ; ) {
|
|
1751
|
+
const l = 2 * i + 1, r = l + 1;
|
|
1752
|
+
let m = i;
|
|
1753
|
+
if (l < this.heap.length && this.less(l, m)) m = l;
|
|
1754
|
+
if (r < this.heap.length && this.less(r, m)) m = r;
|
|
1755
|
+
if (m === i) break;
|
|
1756
|
+
[this.heap[i], this.heap[m]] = [this.heap[m], this.heap[i]];
|
|
1757
|
+
i = m;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
return [top[0], top[2]];
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1243
1764
|
// src/connector.ts
|
|
1244
1765
|
function portPoint(shape, port) {
|
|
1245
1766
|
const r = frame(shape);
|
|
@@ -1383,7 +1904,7 @@ function routeThrough(p, c) {
|
|
|
1383
1904
|
}
|
|
1384
1905
|
}
|
|
1385
1906
|
}
|
|
1386
|
-
function
|
|
1907
|
+
function route2(e, index) {
|
|
1387
1908
|
const c = e.connector;
|
|
1388
1909
|
if (!c) return { kind: "polyline", points: [] };
|
|
1389
1910
|
const [a, b] = endpoints(e, index);
|
|
@@ -1399,16 +1920,19 @@ function route(e, index) {
|
|
|
1399
1920
|
case "elbow": {
|
|
1400
1921
|
const startV = vertical(c.start.port) ?? !horizontal;
|
|
1401
1922
|
const endV = vertical(c.end.port) ?? !horizontal;
|
|
1923
|
+
let plain;
|
|
1402
1924
|
if (startV && endV) {
|
|
1403
1925
|
const my = a.y + dy / 2;
|
|
1404
|
-
|
|
1405
|
-
}
|
|
1406
|
-
if (!startV && !endV) {
|
|
1926
|
+
plain = [a, { x: a.x, y: my }, { x: b.x, y: my }, b];
|
|
1927
|
+
} else if (!startV && !endV) {
|
|
1407
1928
|
const mx = a.x + dx / 2;
|
|
1408
|
-
|
|
1409
|
-
}
|
|
1410
|
-
|
|
1411
|
-
return { kind: "polyline", points:
|
|
1929
|
+
plain = [a, { x: mx, y: a.y }, { x: mx, y: b.y }, b];
|
|
1930
|
+
} else if (startV) plain = [a, { x: a.x, y: b.y }, b];
|
|
1931
|
+
else plain = [a, { x: b.x, y: a.y }, b];
|
|
1932
|
+
if (c.avoid !== true) return { kind: "polyline", points: plain };
|
|
1933
|
+
const ends = new Set([c.start.elementID, c.end.elementID].filter((x) => x !== null));
|
|
1934
|
+
const routed = avoiding(plain, a, b, startV, endV, index.values(), ends);
|
|
1935
|
+
return { kind: "polyline", points: routed === plain ? plain : simplified(routed) };
|
|
1412
1936
|
}
|
|
1413
1937
|
}
|
|
1414
1938
|
}
|
|
@@ -1419,7 +1943,7 @@ function arrowhead(tip, from, size) {
|
|
|
1419
1943
|
return [wing(spread), wing(-spread)];
|
|
1420
1944
|
}
|
|
1421
1945
|
function connectorBounds(e, index) {
|
|
1422
|
-
const pts = routePoints(
|
|
1946
|
+
const pts = routePoints(route2(e, index));
|
|
1423
1947
|
if (pts.length === 0) return { x: 0, y: 0, width: 0, height: 0 };
|
|
1424
1948
|
let x0 = pts[0].x, y0 = pts[0].y, x1 = x0, y1 = y0;
|
|
1425
1949
|
for (const p of pts) {
|
|
@@ -1431,7 +1955,7 @@ function connectorBounds(e, index) {
|
|
|
1431
1955
|
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
1432
1956
|
}
|
|
1433
1957
|
function midpoint(e, index) {
|
|
1434
|
-
const pts = routePoints(
|
|
1958
|
+
const pts = routePoints(route2(e, index));
|
|
1435
1959
|
if (pts.length <= 1) return pts[0] ?? { x: 0, y: 0 };
|
|
1436
1960
|
const lengths = [];
|
|
1437
1961
|
let total = 0;
|
|
@@ -1580,7 +2104,7 @@ function graphOf(frame2) {
|
|
|
1580
2104
|
graphs.set(els, { members: [...els], graph });
|
|
1581
2105
|
return graph;
|
|
1582
2106
|
}
|
|
1583
|
-
var
|
|
2107
|
+
var Frontier2 = class {
|
|
1584
2108
|
constructor() {
|
|
1585
2109
|
__publicField(this, "heap", []);
|
|
1586
2110
|
}
|
|
@@ -1634,7 +2158,7 @@ function grow({ shapes: shapes2, next }, from, offline, until) {
|
|
|
1634
2158
|
const dist = /* @__PURE__ */ new Map([[from, 0]]), order = /* @__PURE__ */ new Map([[from, 0]]);
|
|
1635
2159
|
let counter = 1;
|
|
1636
2160
|
const tree = new FlowTree(from);
|
|
1637
|
-
const open = new
|
|
2161
|
+
const open = new Frontier2();
|
|
1638
2162
|
open.push(from, 0, 0);
|
|
1639
2163
|
while (open.size > 0) {
|
|
1640
2164
|
const { id: n, order: o } = open.pop();
|
|
@@ -1740,6 +2264,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1740
2264
|
return pts;
|
|
1741
2265
|
};
|
|
1742
2266
|
const metrics2 = emptyMetrics();
|
|
2267
|
+
const routes = /* @__PURE__ */ new Map();
|
|
1743
2268
|
for (const e of frame2.elements) {
|
|
1744
2269
|
if (e.kind === "connector" && !e.connector) continue;
|
|
1745
2270
|
metrics2.elements.set(e.id, { throughput: 0, load: null, delay: e.kind !== "connector" ? delayOf(e) : 0, backlog: 0, inFlight: 0, depth: 0 });
|
|
@@ -1751,7 +2276,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1751
2276
|
if (c !== null && Number.isFinite(c)) room.set(e.id, Math.max(c, 0));
|
|
1752
2277
|
}
|
|
1753
2278
|
const full = () => new Set([...room].filter(([, v]) => v <= TINY).map(([id]) => id));
|
|
1754
|
-
const
|
|
2279
|
+
const route3 = (tree, goal) => {
|
|
1755
2280
|
const hops = [], shapes2 = [];
|
|
1756
2281
|
let at = goal;
|
|
1757
2282
|
for (let step = tree.came.get(at); step; step = tree.came.get(at)) {
|
|
@@ -1763,7 +2288,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1763
2288
|
return { hops: hops.reverse(), shapes: shapes2.reverse() };
|
|
1764
2289
|
};
|
|
1765
2290
|
const way = (hops, shapes2) => {
|
|
1766
|
-
const points = [],
|
|
2291
|
+
const points = [], inside2 = [], stretches = [];
|
|
1767
2292
|
let length = 0;
|
|
1768
2293
|
hops.forEach(([id, back], k) => {
|
|
1769
2294
|
const c = byID.get(id);
|
|
@@ -1774,7 +2299,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1774
2299
|
if (last) {
|
|
1775
2300
|
const gap = Math.hypot(line2[0].x - last.x, line2[0].y - last.y);
|
|
1776
2301
|
const shape = byID.get(shapes2[k]);
|
|
1777
|
-
|
|
2302
|
+
inside2.push([length, length + gap, shape ? delayOf(shape) : 0]);
|
|
1778
2303
|
length += gap;
|
|
1779
2304
|
}
|
|
1780
2305
|
const start2 = length;
|
|
@@ -1782,7 +2307,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1782
2307
|
stretches.push([id, start2, length]);
|
|
1783
2308
|
points.push(...line2);
|
|
1784
2309
|
});
|
|
1785
|
-
return length > 0 ? { line: new Polyline(points), length, inside, stretches } : null;
|
|
2310
|
+
return length > 0 ? { line: new Polyline(points), length, inside: inside2, stretches } : null;
|
|
1786
2311
|
};
|
|
1787
2312
|
const along = (w, elapsedAt, speed) => {
|
|
1788
2313
|
let elapsed = elapsedAt, d = 0;
|
|
@@ -1823,6 +2348,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1823
2348
|
if (flow.when !== null && values !== null && !holds(flow.when, values)) continue;
|
|
1824
2349
|
const source = byID.get(flow.from);
|
|
1825
2350
|
const base2 = source ? baseTree(flow.from) : null;
|
|
2351
|
+
routes.set(flow.id, []);
|
|
1826
2352
|
if (!source || !base2) continue;
|
|
1827
2353
|
const volume = amountIn(flow, values);
|
|
1828
2354
|
const fm = { volume, placed: 0, backlog: 0, backup: 0, transit: null };
|
|
@@ -1837,7 +2363,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1837
2363
|
const tree = flowTree(flow.from, frame2, union2(offline, full()), candidates[0]);
|
|
1838
2364
|
const goal2 = tree?.goals(candidates, false)[0];
|
|
1839
2365
|
if (!tree || goal2 === void 0) break;
|
|
1840
|
-
const r =
|
|
2366
|
+
const r = route3(tree, goal2);
|
|
1841
2367
|
const ids = [...r.shapes, ...r.hops.map(([id]) => id)];
|
|
1842
2368
|
const rooms = ids.filter((id) => room.has(id)).map((id) => room.get(id));
|
|
1843
2369
|
const fits = Math.min(left, rooms.length > 0 ? Math.min(...rooms) : Infinity);
|
|
@@ -1850,7 +2376,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1850
2376
|
let at = flow.from;
|
|
1851
2377
|
const goal = base2.goals(candidates, false)[0];
|
|
1852
2378
|
if (goal !== void 0) {
|
|
1853
|
-
const r =
|
|
2379
|
+
const r = route3(base2, goal), done = full();
|
|
1854
2380
|
const sequence = [[r.shapes[0], r.shapes[0]]];
|
|
1855
2381
|
r.hops.forEach(([id], k) => {
|
|
1856
2382
|
sequence.push([id, r.shapes[k]], [r.shapes[k + 1], r.shapes[k]]);
|
|
@@ -1879,6 +2405,13 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1879
2405
|
else waiting2.push([at, left]);
|
|
1880
2406
|
}
|
|
1881
2407
|
waitingBy.set(flow.id, waiting2);
|
|
2408
|
+
routes.set(flow.id, placements.map((p) => ({
|
|
2409
|
+
goal: p.goal,
|
|
2410
|
+
hops: p.hops,
|
|
2411
|
+
amount: p.amount,
|
|
2412
|
+
waits: p.waits === true,
|
|
2413
|
+
cost: p.hops.reduce((s, [id]) => s + routeCost(byID.get(id)?.connector?.cost ?? null), 0)
|
|
2414
|
+
})));
|
|
1882
2415
|
for (const p of placements) {
|
|
1883
2416
|
for (const id of [...p.shapes, ...p.hops.map(([h]) => h)]) bump(id, (m) => {
|
|
1884
2417
|
m.throughput += p.amount;
|
|
@@ -1961,7 +2494,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1961
2494
|
const tree = flowTree(at, frame2, union2(offline, blocked), flow.to[0]);
|
|
1962
2495
|
const goal = tree?.goals(flow.to, false)[0];
|
|
1963
2496
|
if (!tree || goal === void 0) break;
|
|
1964
|
-
const r =
|
|
2497
|
+
const r = route3(tree, goal);
|
|
1965
2498
|
const ids = [...r.shapes.slice(1), ...r.hops.map(([id]) => id)];
|
|
1966
2499
|
const rooms = ids.filter((id) => trial.has(id)).map((id) => trial.get(id));
|
|
1967
2500
|
const spare = rooms.length > 0 ? Math.min(...rooms) : Infinity;
|
|
@@ -2016,7 +2549,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
2016
2549
|
if (c === null || !Number.isFinite(c) || !m) continue;
|
|
2017
2550
|
m.load = c > 0 ? m.throughput / c : 1;
|
|
2018
2551
|
}
|
|
2019
|
-
return { packets, metrics: metrics2 };
|
|
2552
|
+
return { packets, metrics: metrics2, routes };
|
|
2020
2553
|
}
|
|
2021
2554
|
var BLANK = {
|
|
2022
2555
|
seed: 1,
|
|
@@ -2110,16 +2643,16 @@ function finishing(elements, metrics2, values, frame2, flows, since = Infinity,
|
|
|
2110
2643
|
}
|
|
2111
2644
|
var gaugeKey = (id) => "gauge|" + id;
|
|
2112
2645
|
var GAUGE_EASE = 1;
|
|
2113
|
-
var
|
|
2646
|
+
var hex2 = (h) => {
|
|
2114
2647
|
const v = parseInt(h.slice(1), 16);
|
|
2115
2648
|
return { r: (v >> 16 & 255) / 255, g: (v >> 8 & 255) / 255, b: (v & 255) / 255, a: 1 };
|
|
2116
2649
|
};
|
|
2117
|
-
var GAUGE_GREEN =
|
|
2118
|
-
var GAUGE_AMBER =
|
|
2119
|
-
var GAUGE_RED =
|
|
2120
|
-
var GAUGE_BLUE =
|
|
2121
|
-
var GAUGE_TRACK =
|
|
2122
|
-
var GAUGE_GRAY =
|
|
2650
|
+
var GAUGE_GREEN = hex2("#16a34a");
|
|
2651
|
+
var GAUGE_AMBER = hex2("#d97706");
|
|
2652
|
+
var GAUGE_RED = hex2("#dc2626");
|
|
2653
|
+
var GAUGE_BLUE = hex2("#2f6fdf");
|
|
2654
|
+
var GAUGE_TRACK = hex2("#e5e7eb");
|
|
2655
|
+
var GAUGE_GRAY = hex2("#9ca3af");
|
|
2123
2656
|
function formatted(metric, v) {
|
|
2124
2657
|
if (v === null || !Number.isFinite(v)) return "\u2013";
|
|
2125
2658
|
switch (metric) {
|
|
@@ -2405,17 +2938,17 @@ function clusterStrokes(c, angle2, size) {
|
|
|
2405
2938
|
});
|
|
2406
2939
|
}
|
|
2407
2940
|
function contains(contours, p) {
|
|
2408
|
-
let
|
|
2941
|
+
let inside2 = false;
|
|
2409
2942
|
for (const poly of contours) {
|
|
2410
2943
|
if (poly.length < 3) continue;
|
|
2411
2944
|
let k = poly.length - 1;
|
|
2412
2945
|
for (let i = 0; i < poly.length; i++) {
|
|
2413
2946
|
const a = poly[i], b = poly[k];
|
|
2414
|
-
if (a.y > p.y !== b.y > p.y && p.x < (b.x - a.x) * (p.y - a.y) / (b.y - a.y) + a.x)
|
|
2947
|
+
if (a.y > p.y !== b.y > p.y && p.x < (b.x - a.x) * (p.y - a.y) / (b.y - a.y) + a.x) inside2 = !inside2;
|
|
2415
2948
|
k = i;
|
|
2416
2949
|
}
|
|
2417
2950
|
}
|
|
2418
|
-
return
|
|
2951
|
+
return inside2;
|
|
2419
2952
|
}
|
|
2420
2953
|
function distance(p, contours) {
|
|
2421
2954
|
let best = Infinity;
|
|
@@ -2692,7 +3225,7 @@ function connectorParts(e, index, page) {
|
|
|
2692
3225
|
const c = e.connector;
|
|
2693
3226
|
if (!c) return [new Path(), new Path()];
|
|
2694
3227
|
const [a, b] = endpoints(e, index);
|
|
2695
|
-
const r =
|
|
3228
|
+
const r = route2(e, index);
|
|
2696
3229
|
const hops = c.hops === true && page ? hopCrossings(e, r, page, index) : [];
|
|
2697
3230
|
const key = [
|
|
2698
3231
|
e.seed,
|
|
@@ -2780,7 +3313,7 @@ function linesBeneath(e, page, index) {
|
|
|
2780
3313
|
}
|
|
2781
3314
|
const count = f.first.get(e.id) ?? f.under.length;
|
|
2782
3315
|
while (f.lines.length < count) {
|
|
2783
|
-
const points = routePoints(
|
|
3316
|
+
const points = routePoints(route2(f.under[f.lines.length], index));
|
|
2784
3317
|
f.lines.push({ points, box: boxOf(points) });
|
|
2785
3318
|
}
|
|
2786
3319
|
return { lines: f.lines, count };
|
|
@@ -2794,7 +3327,7 @@ function hopCrossings(e, r, page, index) {
|
|
|
2794
3327
|
for (const samples of segments) {
|
|
2795
3328
|
const along = runningLengths(samples);
|
|
2796
3329
|
const length = along[along.length - 1];
|
|
2797
|
-
const
|
|
3330
|
+
const found2 = [];
|
|
2798
3331
|
const near = [];
|
|
2799
3332
|
const whole = boxOf(samples);
|
|
2800
3333
|
for (let n = 0; n < count; n++) if (touching(whole, lines[n].box)) near.push(lines[n]);
|
|
@@ -2811,12 +3344,12 @@ function hopCrossings(e, r, page, index) {
|
|
|
2811
3344
|
if (Math.abs(det) < 1e-9) continue;
|
|
2812
3345
|
const wx = a.x - p.x, wy = a.y - p.y;
|
|
2813
3346
|
const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
|
|
2814
|
-
if (t > 0 && t < 1 && s >= 0 && s <= 1)
|
|
3347
|
+
if (t > 0 && t < 1 && s >= 0 && s <= 1) found2.push(along[i] + t * (along[i + 1] - along[i]));
|
|
2815
3348
|
}
|
|
2816
3349
|
}
|
|
2817
3350
|
}
|
|
2818
3351
|
const kept = [];
|
|
2819
|
-
for (const at of
|
|
3352
|
+
for (const at of found2.sort((x, y) => x - y)) {
|
|
2820
3353
|
if (at < rad + 2 || at > length - rad - 2) continue;
|
|
2821
3354
|
if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
|
|
2822
3355
|
kept.push(at);
|
|
@@ -3030,7 +3563,7 @@ var lerpColor = (a, b, u) => ({
|
|
|
3030
3563
|
b: mix01(a.b, b.b, u),
|
|
3031
3564
|
a: mix01(a.a, b.a, u)
|
|
3032
3565
|
});
|
|
3033
|
-
function
|
|
3566
|
+
function num3(track, t, p, base2) {
|
|
3034
3567
|
return value(track, t, (k) => k[p], base2, lerpNum);
|
|
3035
3568
|
}
|
|
3036
3569
|
function isChanging(track, t) {
|
|
@@ -3056,20 +3589,20 @@ function isVisible(track, t) {
|
|
|
3056
3589
|
function applyTrack(track, e, t) {
|
|
3057
3590
|
e = cloneElement(e);
|
|
3058
3591
|
if (track.keys.some((k) => k.reveal !== void 0)) {
|
|
3059
|
-
const r =
|
|
3592
|
+
const r = num3(track, t, "reveal", e.reveal ?? 1);
|
|
3060
3593
|
e.reveal = r >= 1 ? null : Math.max(0, r);
|
|
3061
3594
|
}
|
|
3062
3595
|
if (track.keys.some((k) => k.opacity !== void 0)) {
|
|
3063
|
-
const o = Math.min(Math.max(
|
|
3596
|
+
const o = Math.min(Math.max(num3(track, t, "opacity", e.style.opacity ?? 1), 0), 1);
|
|
3064
3597
|
e.style.opacity = o >= 1 ? null : o;
|
|
3065
3598
|
}
|
|
3066
3599
|
if (e.kind === "connector") return e;
|
|
3067
|
-
e.x =
|
|
3068
|
-
e.y =
|
|
3069
|
-
e.width = Math.max(0,
|
|
3070
|
-
e.height = Math.max(0,
|
|
3071
|
-
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation =
|
|
3072
|
-
e.style.strokeWidth = Math.max(0,
|
|
3600
|
+
e.x = num3(track, t, "x", e.x);
|
|
3601
|
+
e.y = num3(track, t, "y", e.y);
|
|
3602
|
+
e.width = Math.max(0, num3(track, t, "width", e.width));
|
|
3603
|
+
e.height = Math.max(0, num3(track, t, "height", e.height));
|
|
3604
|
+
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation = num3(track, t, "rotation", angle(e));
|
|
3605
|
+
e.style.strokeWidth = Math.max(0, num3(track, t, "strokeWidth", e.style.strokeWidth));
|
|
3073
3606
|
e.style.stroke = value(track, t, (k) => k.stroke, e.style.stroke, lerpColor);
|
|
3074
3607
|
e.style.fill = value(track, t, (k) => k.fill, e.style.fill, lerpColor);
|
|
3075
3608
|
return e;
|
|
@@ -3084,7 +3617,7 @@ function followSpan(track, t) {
|
|
|
3084
3617
|
return f;
|
|
3085
3618
|
}
|
|
3086
3619
|
function tracing(e, page, index = indexOf(page)) {
|
|
3087
|
-
if (e.kind === "connector") return new Polyline(routePoints(
|
|
3620
|
+
if (e.kind === "connector") return new Polyline(routePoints(route2(e, index)));
|
|
3088
3621
|
const clean = cloneElement(e);
|
|
3089
3622
|
clean.style.roughness = 0;
|
|
3090
3623
|
const pts = outline(clean).contours()[0] ?? [];
|
|
@@ -3145,7 +3678,8 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3145
3678
|
}
|
|
3146
3679
|
const flowing = (page.flows ?? []).length > 0;
|
|
3147
3680
|
const measuring = flowing || page.elements.some((e) => e.gauge || (e.overrides ?? []).some((o) => mentionsMetric(o.when)));
|
|
3148
|
-
const
|
|
3681
|
+
const fills = values !== null && Object.keys(values).length > 0 && page.elements.some((e) => e.text?.includes("{") || (e.overrides ?? []).some((o) => o.text?.includes("{")));
|
|
3682
|
+
const timeline = page.timeline ?? (off.size === 0 && active.size === 0 && !measuring && !fills ? null : EMPTY_TIMELINE);
|
|
3149
3683
|
if (!timeline) return { frame: page, metrics: emptyMetrics() };
|
|
3150
3684
|
t = clampTime(t);
|
|
3151
3685
|
const tracks = trackMap(timeline);
|
|
@@ -3181,6 +3715,10 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3181
3715
|
const track = tracks.get(e.id);
|
|
3182
3716
|
let out = track ? applyTrack(track, e, t) : e;
|
|
3183
3717
|
for (const o of active.get(e.id) ?? []) out = applyOverride(o, out);
|
|
3718
|
+
if (fills && values && out.text) {
|
|
3719
|
+
const text = filling(out.text, values);
|
|
3720
|
+
if (text !== out.text) out = { ...out, text };
|
|
3721
|
+
}
|
|
3184
3722
|
const list = active.get(e.id);
|
|
3185
3723
|
if (list) {
|
|
3186
3724
|
const caps = list.map((o) => o.capacity).filter((c) => c !== null && Number.isFinite(c));
|
|
@@ -3364,10 +3902,10 @@ var Expansions = class {
|
|
|
3364
3902
|
this.posing = posing;
|
|
3365
3903
|
}
|
|
3366
3904
|
};
|
|
3367
|
-
function expandingSymbols(page, t, posing = true, times = /* @__PURE__ */ new Map(),
|
|
3905
|
+
function expandingSymbols(page, t, posing = true, times = /* @__PURE__ */ new Map(), memo2) {
|
|
3368
3906
|
if (!hasInstances(page)) return page;
|
|
3369
3907
|
const budget = { left: MAX_ELEMENTS };
|
|
3370
|
-
const m =
|
|
3908
|
+
const m = memo2 && memo2.page === page && memo2.t === t && memo2.posing === posing ? memo2 : void 0;
|
|
3371
3909
|
return { ...page, elements: expand(page.elements, page, t, symbolTable(page), [], posing, budget, times, m) };
|
|
3372
3910
|
}
|
|
3373
3911
|
function symbolTable(page) {
|
|
@@ -3375,7 +3913,7 @@ function symbolTable(page) {
|
|
|
3375
3913
|
for (const s of page.symbols ?? []) if (!table.has(s.id)) table.set(s.id, s);
|
|
3376
3914
|
return table;
|
|
3377
3915
|
}
|
|
3378
|
-
function expand(elements, page, t, table, stack, posing, budget, times = /* @__PURE__ */ new Map(),
|
|
3916
|
+
function expand(elements, page, t, table, stack, posing, budget, times = /* @__PURE__ */ new Map(), memo2) {
|
|
3379
3917
|
const instances = new Set(elements.filter((e) => e.kind === "symbol").map((e) => e.id));
|
|
3380
3918
|
if (instances.size === 0) {
|
|
3381
3919
|
budget.left -= elements.length;
|
|
@@ -3401,7 +3939,7 @@ function expand(elements, page, t, table, stack, posing, budget, times = /* @__P
|
|
|
3401
3939
|
continue;
|
|
3402
3940
|
}
|
|
3403
3941
|
const override0 = stack.length === 0 ? times.get(e.id) : void 0;
|
|
3404
|
-
const was =
|
|
3942
|
+
const was = memo2?.made.get(e);
|
|
3405
3943
|
if (was && was.override === override0 && was.after > 0 && budget.left - (was.before - was.after) > 0) {
|
|
3406
3944
|
budget.left -= was.before - was.after;
|
|
3407
3945
|
for (const x of was.out) out.push(x);
|
|
@@ -3423,7 +3961,7 @@ function expand(elements, page, t, table, stack, posing, budget, times = /* @__P
|
|
|
3423
3961
|
layers: [{ id: e.layerID, name: "", visible: true, locked: false }]
|
|
3424
3962
|
};
|
|
3425
3963
|
for (const x of expand(drawn.elements, drawn, u, table, [...stack, id], posing, budget)) out.push(placed(x, e, symbol));
|
|
3426
|
-
if (
|
|
3964
|
+
if (memo2 && stack.length === 0 && !was) memo2.made.set(e, { override: override0, before, after: budget.left, out: out.slice(from) });
|
|
3427
3965
|
}
|
|
3428
3966
|
return out;
|
|
3429
3967
|
}
|
|
@@ -3476,8 +4014,8 @@ function mix2(x) {
|
|
|
3476
4014
|
return z ^ z >> 31n;
|
|
3477
4015
|
}
|
|
3478
4016
|
function halves(id) {
|
|
3479
|
-
const
|
|
3480
|
-
return [BigInt(`0x${
|
|
4017
|
+
const hex3 = id.replace(/-/g, "").padStart(32, "0").slice(-32);
|
|
4018
|
+
return [BigInt(`0x${hex3.slice(0, 16)}`), BigInt(`0x${hex3.slice(16)}`)];
|
|
3481
4019
|
}
|
|
3482
4020
|
function derivedID(instance, inner) {
|
|
3483
4021
|
const [a, b] = halves(instance), [c, d] = halves(inner);
|
|
@@ -3498,7 +4036,12 @@ function clickTargets(page, file) {
|
|
|
3498
4036
|
let pageOf = null;
|
|
3499
4037
|
for (const e of page.elements) {
|
|
3500
4038
|
const c = e.click;
|
|
3501
|
-
const state = c && changesState(c) ? {
|
|
4039
|
+
const state = c && changesState(c) ? {
|
|
4040
|
+
...c.set ? { set: c.set } : {},
|
|
4041
|
+
...c.toggle ? { toggle: c.toggle } : {},
|
|
4042
|
+
...c.reset ? { reset: true } : {},
|
|
4043
|
+
...c.run ? { run: c.run } : {}
|
|
4044
|
+
} : null;
|
|
3502
4045
|
if (c?.scene) {
|
|
3503
4046
|
const i = sceneNamed(c.scene, markers);
|
|
3504
4047
|
if (i >= 0) {
|
|
@@ -3805,12 +4348,23 @@ function inline(src, style2 = {}) {
|
|
|
3805
4348
|
i += m[0].length;
|
|
3806
4349
|
continue;
|
|
3807
4350
|
}
|
|
3808
|
-
if (m = /^(
|
|
4351
|
+
if (m = /^(\*\*\*|___)(?=\S)([\s\S]*?\S)\1(?!\*|_)/.exec(rest)) {
|
|
3809
4352
|
emit();
|
|
3810
|
-
out.push(...inline(m[2], { ...style2, bold: true }));
|
|
4353
|
+
out.push(...inline(m[2], { ...style2, bold: true, italic: true }));
|
|
3811
4354
|
i += m[0].length;
|
|
3812
4355
|
continue;
|
|
3813
4356
|
}
|
|
4357
|
+
if (m = /^(\*\*|__)(?=\S)([\s\S]*?\S)\1/.exec(rest)) {
|
|
4358
|
+
let body = m[2], length = m[0].length;
|
|
4359
|
+
if (m[1] === "**" && rest[length] === "*" && (body.replace(/\*\*/g, "").match(/\*/g) ?? []).length % 2 === 1) {
|
|
4360
|
+
body += "*";
|
|
4361
|
+
length++;
|
|
4362
|
+
}
|
|
4363
|
+
emit();
|
|
4364
|
+
out.push(...inline(body, { ...style2, bold: true }));
|
|
4365
|
+
i += length;
|
|
4366
|
+
continue;
|
|
4367
|
+
}
|
|
3814
4368
|
if ((m = /^(\*|_)(?=\S)([\s\S]*?\S)\1(?!\1)/.exec(rest)) && (m[1] === "*" || !/\w/.test(src[i - 1] ?? ""))) {
|
|
3815
4369
|
emit();
|
|
3816
4370
|
out.push(...inline(m[2], { ...style2, italic: true }));
|
|
@@ -4194,20 +4748,6 @@ function sameStyle(a, b) {
|
|
|
4194
4748
|
return a === b || a.size === b.size && a.bold === b.bold && a.italic === b.italic && a.mono === b.mono && a.strike === b.strike && a.underline === b.underline && a.alpha === b.alpha;
|
|
4195
4749
|
}
|
|
4196
4750
|
|
|
4197
|
-
// src/theme.ts
|
|
4198
|
-
var rgb = (r, g, b) => ({ r, g, b, a: 1 });
|
|
4199
|
-
var themes = [
|
|
4200
|
-
{ id: "paper", name: "Paper", background: rgb(0.984, 0.98, 0.965), ink: rgb(0.12, 0.12, 0.13) },
|
|
4201
|
-
{ id: "white", name: "White", background: rgb(1, 1, 1), ink: rgb(0.12, 0.12, 0.13) },
|
|
4202
|
-
{ id: "sepia", name: "Sepia", background: rgb(0.953, 0.914, 0.824), ink: rgb(0.23, 0.18, 0.13) },
|
|
4203
|
-
{ id: "blueprint", name: "Blueprint", background: rgb(0.106, 0.227, 0.361), ink: rgb(0.91, 0.945, 0.984) },
|
|
4204
|
-
{ id: "chalkboard", name: "Chalkboard", background: rgb(0.169, 0.227, 0.2), ink: rgb(0.945, 0.945, 0.918) },
|
|
4205
|
-
{ id: "midnight", name: "Midnight", background: rgb(0.082, 0.086, 0.102), ink: rgb(0.925, 0.925, 0.94) }
|
|
4206
|
-
];
|
|
4207
|
-
function themeNamed(id) {
|
|
4208
|
-
return themes.find((t) => t.id === id) ?? null;
|
|
4209
|
-
}
|
|
4210
|
-
|
|
4211
4751
|
// src/svg.ts
|
|
4212
4752
|
function bounds(page, padding = 24) {
|
|
4213
4753
|
if (hasInstances(page)) return bounds(expandingSymbols(page, 0), padding);
|
|
@@ -4273,7 +4813,7 @@ function svgOpen(b, background, paint) {
|
|
|
4273
4813
|
}
|
|
4274
4814
|
function svgParts(page, background, fixed, links = false, only) {
|
|
4275
4815
|
if (hasInstances(page)) return svgParts(expandingSymbols(page, 0), background, fixed, links, only);
|
|
4276
|
-
const theme =
|
|
4816
|
+
const theme = pageTheme(page);
|
|
4277
4817
|
const bg = background ? theme?.background ?? background : null;
|
|
4278
4818
|
const ink = theme?.ink ?? black;
|
|
4279
4819
|
const d = { ...page, elements: page.elements.map((e) => resolvingInk(e, ink)) };
|
|
@@ -4309,7 +4849,7 @@ ${body}</a>
|
|
|
4309
4849
|
}
|
|
4310
4850
|
function svg(page, background = { r: 1, g: 1, b: 1, a: 1 }, fixed) {
|
|
4311
4851
|
const { bounds: b, parts } = svgParts(page, background, fixed, true);
|
|
4312
|
-
return svgOpen(b,
|
|
4852
|
+
return svgOpen(b, pageTheme(page)?.background ?? background, background !== null) + parts.map((p) => p.markup).join("") + "</svg>\n";
|
|
4313
4853
|
}
|
|
4314
4854
|
function labelRect(e, index) {
|
|
4315
4855
|
const m = midpoint(e, index);
|
|
@@ -4403,7 +4943,7 @@ function svgElement(e, index, labelBackground, page) {
|
|
|
4403
4943
|
}
|
|
4404
4944
|
s += `<path d="${outline(e).data()}" fill="none" ${strokeAttrs}/>
|
|
4405
4945
|
`;
|
|
4406
|
-
if (text) s += e.kind === "note" ? noteMarkup(text, e) : textElement(text, e.kind === "path" ? pathLabelBox(e) : frame(e), e, true);
|
|
4946
|
+
if (text) s += e.kind === "note" ? noteMarkup(text, e) : textElement(text, e.kind === "path" || e.kind === "rectangle" ? pathLabelBox(e) : frame(e), e, true);
|
|
4407
4947
|
return s;
|
|
4408
4948
|
}
|
|
4409
4949
|
default:
|
|
@@ -4539,6 +5079,40 @@ function servePlans() {
|
|
|
4539
5079
|
});
|
|
4540
5080
|
}
|
|
4541
5081
|
|
|
5082
|
+
// src/logic-loader.ts
|
|
5083
|
+
var scriptSrc = typeof document !== "undefined" ? document.currentScript?.src ?? null : null;
|
|
5084
|
+
var found = null;
|
|
5085
|
+
var present = () => globalThis.DottieLogic;
|
|
5086
|
+
function logicEngine() {
|
|
5087
|
+
found ?? (found = (async () => {
|
|
5088
|
+
if (!present()) {
|
|
5089
|
+
const here = import.meta.url;
|
|
5090
|
+
const base2 = here ?? scriptSrc;
|
|
5091
|
+
if (!base2) throw new Error("No dottie-logic.min.js on the page, and no address to find it from");
|
|
5092
|
+
const url = new URL("./dottie-logic.min.js", base2).href;
|
|
5093
|
+
if (here) await import(
|
|
5094
|
+
/* @vite-ignore */
|
|
5095
|
+
url
|
|
5096
|
+
);
|
|
5097
|
+
else await new Promise((resolve2, reject) => {
|
|
5098
|
+
const s = document.createElement("script");
|
|
5099
|
+
s.src = url;
|
|
5100
|
+
s.onload = () => resolve2();
|
|
5101
|
+
s.onerror = () => reject(new Error(`Couldn't load ${url}`));
|
|
5102
|
+
document.head.append(s);
|
|
5103
|
+
});
|
|
5104
|
+
}
|
|
5105
|
+
const engine = present();
|
|
5106
|
+
if (!engine) throw new Error("dottie-logic.min.js didn't load");
|
|
5107
|
+
await engine.loadLogic();
|
|
5108
|
+
return engine;
|
|
5109
|
+
})());
|
|
5110
|
+
found.catch(() => {
|
|
5111
|
+
found = null;
|
|
5112
|
+
});
|
|
5113
|
+
return found;
|
|
5114
|
+
}
|
|
5115
|
+
|
|
4542
5116
|
// src/player.ts
|
|
4543
5117
|
var WHITE = { r: 1, g: 1, b: 1, a: 1 };
|
|
4544
5118
|
function pageIndex(file, spec) {
|
|
@@ -4646,7 +5220,7 @@ var Playback = class {
|
|
|
4646
5220
|
this.sceneNames = (page.timeline?.markers ?? []).map((m) => m.name);
|
|
4647
5221
|
this.sceneLoops = (page.timeline?.markers ?? []).map((m) => m.loop === true);
|
|
4648
5222
|
this.state = { ...page.variables ?? {} };
|
|
4649
|
-
const theme =
|
|
5223
|
+
const theme = pageTheme(page);
|
|
4650
5224
|
this.background = theme?.background ?? WHITE;
|
|
4651
5225
|
this.ink = theme?.ink ?? { r: 0.12, g: 0.12, b: 0.13, a: 1 };
|
|
4652
5226
|
const ns = "http://www.w3.org/2000/svg";
|
|
@@ -4880,6 +5454,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
4880
5454
|
__publicField(this, "stillTime", null);
|
|
4881
5455
|
__publicField(this, "visible", true);
|
|
4882
5456
|
__publicField(this, "observer", null);
|
|
5457
|
+
/** Runs handler `handler` from the page's logic for a click on `id` (DottieDocument.clicked). Clicks take turns, so
|
|
5458
|
+
* each handler sees the state the one before left. */
|
|
5459
|
+
__publicField(this, "logicQueue", Promise.resolve());
|
|
4883
5460
|
this.root = this.attachShadow({ mode: "open" });
|
|
4884
5461
|
this.root.innerHTML = `<style>${STYLE}</style><div class="stage" part="stage" role="img"></div>
|
|
4885
5462
|
<div class="bar" part="controls" hidden>
|
|
@@ -5106,7 +5683,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5106
5683
|
this.scheduleReplay();
|
|
5107
5684
|
}
|
|
5108
5685
|
if (!click) return;
|
|
5109
|
-
if (click.
|
|
5686
|
+
if (click.run && p.page.logic && this.hasAttribute("allow-logic")) {
|
|
5687
|
+
this.runLogic(click.run, id, click.reset === true);
|
|
5688
|
+
} else if (click.set || click.toggle || click.reset) {
|
|
5110
5689
|
this.setState(applyClick(
|
|
5111
5690
|
{ scene: null, url: null, set: click.set ?? null, toggle: click.toggle ?? null, reset: click.reset ?? null },
|
|
5112
5691
|
p.state,
|
|
@@ -5128,6 +5707,35 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5128
5707
|
}
|
|
5129
5708
|
}
|
|
5130
5709
|
}
|
|
5710
|
+
runLogic(handler, id, reset) {
|
|
5711
|
+
const p = this.playback;
|
|
5712
|
+
if (!p) return;
|
|
5713
|
+
const e = p.page.elements.find((x) => x.id === id);
|
|
5714
|
+
const s = p.sceneAt(p.frame);
|
|
5715
|
+
const event = {
|
|
5716
|
+
id,
|
|
5717
|
+
anchor: e?.anchor ?? null,
|
|
5718
|
+
text: e?.text?.split("\n")[0] ?? null,
|
|
5719
|
+
time: this.currentTime,
|
|
5720
|
+
scene: s < 0 ? null : p.sceneNames[s]
|
|
5721
|
+
};
|
|
5722
|
+
this.logicQueue = this.logicQueue.then(async () => {
|
|
5723
|
+
const engine = await logicEngine();
|
|
5724
|
+
const now = this.playback;
|
|
5725
|
+
if (!now || now !== p || !now.page.logic) return;
|
|
5726
|
+
let start2 = now.state;
|
|
5727
|
+
if (reset) {
|
|
5728
|
+
start2 = {};
|
|
5729
|
+
for (const k of Object.keys(now.state)) start2[k] = false;
|
|
5730
|
+
Object.assign(start2, now.page.variables ?? {});
|
|
5731
|
+
}
|
|
5732
|
+
const r = engine.runLogic(now.page.logic, handler, start2, event);
|
|
5733
|
+
if (r.ok) this.setState({ ...start2, ...r.set });
|
|
5734
|
+
else this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: r.error } }));
|
|
5735
|
+
}).catch((err) => {
|
|
5736
|
+
this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: err.message } }));
|
|
5737
|
+
});
|
|
5738
|
+
}
|
|
5131
5739
|
/** The page's variables now. */
|
|
5132
5740
|
get variables() {
|
|
5133
5741
|
return { ...this.playback?.state ?? {} };
|
|
@@ -5234,7 +5842,7 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5234
5842
|
if (handled) e.preventDefault();
|
|
5235
5843
|
}
|
|
5236
5844
|
};
|
|
5237
|
-
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label"]);
|
|
5845
|
+
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label", "allow-logic"]);
|
|
5238
5846
|
function clickable(target) {
|
|
5239
5847
|
const el = target;
|
|
5240
5848
|
return el && typeof el.closest === "function" ? el.closest(".dottie-a") : null;
|
|
@@ -5300,7 +5908,7 @@ function inspectFrame(page, t) {
|
|
|
5300
5908
|
const [a, b] = endpoints(e, index);
|
|
5301
5909
|
d.start = pt(a);
|
|
5302
5910
|
d.end = pt(b);
|
|
5303
|
-
d.route = routePoints(
|
|
5911
|
+
d.route = routePoints(route2(e, index)).map(pt);
|
|
5304
5912
|
}
|
|
5305
5913
|
return d;
|
|
5306
5914
|
});
|
|
@@ -5339,7 +5947,7 @@ function render(source, options = {}) {
|
|
|
5339
5947
|
function isFile(v) {
|
|
5340
5948
|
return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
|
|
5341
5949
|
}
|
|
5342
|
-
var VERSION = "0.
|
|
5950
|
+
var VERSION = "0.11.0";
|
|
5343
5951
|
|
|
5344
5952
|
// src/player-entry.ts
|
|
5345
5953
|
servePlans();
|
|
@@ -5380,6 +5988,8 @@ export {
|
|
|
5380
5988
|
luminance,
|
|
5381
5989
|
orderedElements,
|
|
5382
5990
|
pageIndex,
|
|
5991
|
+
pageTheme,
|
|
5992
|
+
paletteColor,
|
|
5383
5993
|
parseDottie,
|
|
5384
5994
|
plan,
|
|
5385
5995
|
posterTime,
|