@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.js
CHANGED
|
@@ -1,84 +1,334 @@
|
|
|
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 __publicField = (obj, key, value2) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value2);
|
|
5
5
|
|
|
6
|
-
// src/
|
|
6
|
+
// src/expression.ts
|
|
7
|
+
var METRIC_NAMES = ["throughput", "load", "delay", "backlog", "depth", "inFlight", "transit", "backup"];
|
|
8
|
+
var FUNCTIONS = { min: null, max: null, abs: 1, floor: 1, ceil: 1, round: 1, clamp: 3 };
|
|
7
9
|
var MAX_NAME = 64;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
var truthy = (v) => typeof v === "boolean" ? v : v !== 0 && !Number.isNaN(v);
|
|
11
|
+
var num = (v) => typeof v === "boolean" ? v ? 1 : 0 : v;
|
|
12
|
+
var parsed = /* @__PURE__ */ new Map();
|
|
13
|
+
function parseExpr(text) {
|
|
14
|
+
const hit = parsed.get(text);
|
|
15
|
+
if (hit !== void 0) return hit;
|
|
16
|
+
const p = new Parser(text);
|
|
17
|
+
let e = p.or();
|
|
18
|
+
if (e && !p.atEnd()) e = null;
|
|
19
|
+
if (parsed.size > 4096) parsed.clear();
|
|
20
|
+
parsed.set(text, e);
|
|
21
|
+
return e;
|
|
10
22
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
23
|
+
function evaluate(e, state, metric = null) {
|
|
24
|
+
switch (e.k) {
|
|
25
|
+
case "lit":
|
|
26
|
+
return e.v;
|
|
27
|
+
case "name":
|
|
28
|
+
return state[e.name] ?? false;
|
|
29
|
+
case "metric":
|
|
30
|
+
return metric?.(e.metric, e.target) ?? NaN;
|
|
31
|
+
case "not":
|
|
32
|
+
return !truthy(evaluate(e.e, state, metric));
|
|
33
|
+
case "neg":
|
|
34
|
+
return -num(evaluate(e.e, state, metric));
|
|
35
|
+
case "call": {
|
|
36
|
+
const xs = e.args.map((a) => num(evaluate(a, state, metric)));
|
|
37
|
+
const fold = (better) => {
|
|
38
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
39
|
+
let out = xs[0];
|
|
40
|
+
for (const x of xs.slice(1)) if (better(x, out)) out = x;
|
|
41
|
+
return out;
|
|
42
|
+
};
|
|
43
|
+
switch (e.fn) {
|
|
44
|
+
case "min":
|
|
45
|
+
return fold((a, b) => a < b);
|
|
46
|
+
case "max":
|
|
47
|
+
return fold((a, b) => a > b);
|
|
48
|
+
case "abs":
|
|
49
|
+
return Math.abs(xs[0]);
|
|
50
|
+
case "floor":
|
|
51
|
+
return Math.floor(xs[0]);
|
|
52
|
+
case "ceil":
|
|
53
|
+
return Math.ceil(xs[0]);
|
|
54
|
+
case "round":
|
|
55
|
+
return Math.floor(xs[0] + 0.5);
|
|
56
|
+
default:
|
|
57
|
+
if (xs.some(Number.isNaN)) return NaN;
|
|
58
|
+
return xs[0] < xs[1] ? xs[1] : xs[0] > xs[2] ? xs[2] : xs[0];
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
case "bin": {
|
|
62
|
+
if (e.op === "&&") return truthy(evaluate(e.l, state, metric)) && truthy(evaluate(e.r, state, metric));
|
|
63
|
+
if (e.op === "||") return truthy(evaluate(e.l, state, metric)) || truthy(evaluate(e.r, state, metric));
|
|
64
|
+
const a = evaluate(e.l, state, metric), b = evaluate(e.r, state, metric);
|
|
65
|
+
const x = num(a), y = num(b);
|
|
66
|
+
switch (e.op) {
|
|
67
|
+
case "+":
|
|
68
|
+
return x + y;
|
|
69
|
+
case "-":
|
|
70
|
+
return x - y;
|
|
71
|
+
case "*":
|
|
72
|
+
return x * y;
|
|
73
|
+
case "/":
|
|
74
|
+
return x / y;
|
|
75
|
+
case "%":
|
|
76
|
+
return x % y;
|
|
77
|
+
}
|
|
78
|
+
if (Number.isNaN(x) || Number.isNaN(y)) return false;
|
|
79
|
+
switch (e.op) {
|
|
80
|
+
case "<":
|
|
81
|
+
return x < y;
|
|
82
|
+
case "<=":
|
|
83
|
+
return x <= y;
|
|
84
|
+
case ">":
|
|
85
|
+
return x > y;
|
|
86
|
+
case ">=":
|
|
87
|
+
return x >= y;
|
|
88
|
+
case "==":
|
|
89
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a === b : x === y;
|
|
90
|
+
default:
|
|
91
|
+
return typeof a === "boolean" && typeof b === "boolean" ? a !== b : x !== y;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
48
94
|
}
|
|
49
95
|
}
|
|
50
|
-
function
|
|
51
|
-
|
|
96
|
+
function exprNames(e) {
|
|
97
|
+
const out = [];
|
|
98
|
+
const walk = (x) => {
|
|
99
|
+
switch (x.k) {
|
|
100
|
+
case "name":
|
|
101
|
+
if (!out.includes(x.name)) out.push(x.name);
|
|
102
|
+
break;
|
|
103
|
+
case "not":
|
|
104
|
+
case "neg":
|
|
105
|
+
walk(x.e);
|
|
106
|
+
break;
|
|
107
|
+
case "bin":
|
|
108
|
+
walk(x.l);
|
|
109
|
+
walk(x.r);
|
|
110
|
+
break;
|
|
111
|
+
case "call":
|
|
112
|
+
x.args.forEach(walk);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
walk(e);
|
|
117
|
+
return out;
|
|
52
118
|
}
|
|
53
|
-
function
|
|
119
|
+
function exprMetrics(e) {
|
|
54
120
|
const out = [];
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
121
|
+
const walk = (x) => {
|
|
122
|
+
switch (x.k) {
|
|
123
|
+
case "metric":
|
|
124
|
+
out.push({ metric: x.metric, target: x.target });
|
|
125
|
+
break;
|
|
126
|
+
case "not":
|
|
127
|
+
case "neg":
|
|
128
|
+
walk(x.e);
|
|
129
|
+
break;
|
|
130
|
+
case "bin":
|
|
131
|
+
walk(x.l);
|
|
132
|
+
walk(x.r);
|
|
133
|
+
break;
|
|
134
|
+
case "call":
|
|
135
|
+
x.args.forEach(walk);
|
|
136
|
+
break;
|
|
62
137
|
}
|
|
63
|
-
}
|
|
138
|
+
};
|
|
139
|
+
walk(e);
|
|
64
140
|
return out;
|
|
65
141
|
}
|
|
142
|
+
var isDigit = (c) => c !== void 0 && c >= "0" && c <= "9";
|
|
143
|
+
var isLetter = (c) => c !== void 0 && (c === "_" || c >= "a" && c <= "z" || c >= "A" && c <= "Z");
|
|
144
|
+
var Parser = class {
|
|
145
|
+
constructor(text) {
|
|
146
|
+
__publicField(this, "s");
|
|
147
|
+
__publicField(this, "i", 0);
|
|
148
|
+
this.s = [...text];
|
|
149
|
+
}
|
|
150
|
+
atEnd() {
|
|
151
|
+
this.skip();
|
|
152
|
+
return this.i === this.s.length;
|
|
153
|
+
}
|
|
154
|
+
skip() {
|
|
155
|
+
while (this.i < this.s.length && " \n\r".includes(this.s[this.i])) this.i++;
|
|
156
|
+
}
|
|
157
|
+
eat(token) {
|
|
158
|
+
this.skip();
|
|
159
|
+
const t = [...token];
|
|
160
|
+
if (this.i + t.length > this.s.length) return false;
|
|
161
|
+
for (let k = 0; k < t.length; k++) if (this.s[this.i + k] !== t[k]) return false;
|
|
162
|
+
if (t.length === 1 && this.s[this.i + 1] === "=" && "<>!=".includes(t[0])) return false;
|
|
163
|
+
this.i += t.length;
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
or() {
|
|
167
|
+
let l = this.and();
|
|
168
|
+
if (!l) return null;
|
|
169
|
+
while (this.eat("||")) {
|
|
170
|
+
const r = this.and();
|
|
171
|
+
if (!r) return null;
|
|
172
|
+
l = { k: "bin", op: "||", l, r };
|
|
173
|
+
}
|
|
174
|
+
return l;
|
|
175
|
+
}
|
|
176
|
+
and() {
|
|
177
|
+
let l = this.compare();
|
|
178
|
+
if (!l) return null;
|
|
179
|
+
while (this.eat("&&")) {
|
|
180
|
+
const r = this.compare();
|
|
181
|
+
if (!r) return null;
|
|
182
|
+
l = { k: "bin", op: "&&", l, r };
|
|
183
|
+
}
|
|
184
|
+
return l;
|
|
185
|
+
}
|
|
186
|
+
compare() {
|
|
187
|
+
const l = this.sum();
|
|
188
|
+
if (!l) return null;
|
|
189
|
+
for (const op of ["<=", ">=", "==", "!=", "<", ">"]) {
|
|
190
|
+
if (this.eat(op)) {
|
|
191
|
+
const r = this.sum();
|
|
192
|
+
return r ? { k: "bin", op, l, r } : null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return l;
|
|
196
|
+
}
|
|
197
|
+
sum() {
|
|
198
|
+
let l = this.product();
|
|
199
|
+
if (!l) return null;
|
|
200
|
+
for (; ; ) {
|
|
201
|
+
const op = this.eat("+") ? "+" : this.eat("-") ? "-" : null;
|
|
202
|
+
if (!op) return l;
|
|
203
|
+
const r = this.product();
|
|
204
|
+
if (!r) return null;
|
|
205
|
+
l = { k: "bin", op, l, r };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
product() {
|
|
209
|
+
let l = this.unary();
|
|
210
|
+
if (!l) return null;
|
|
211
|
+
for (; ; ) {
|
|
212
|
+
const op = ["*", "/", "%"].find((o) => this.eat(o));
|
|
213
|
+
if (!op) return l;
|
|
214
|
+
const r = this.unary();
|
|
215
|
+
if (!r) return null;
|
|
216
|
+
l = { k: "bin", op, l, r };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
unary() {
|
|
220
|
+
if (this.eat("!")) {
|
|
221
|
+
const e = this.unary();
|
|
222
|
+
return e ? { k: "not", e } : null;
|
|
223
|
+
}
|
|
224
|
+
if (this.eat("-")) {
|
|
225
|
+
const e = this.unary();
|
|
226
|
+
return e ? { k: "neg", e } : null;
|
|
227
|
+
}
|
|
228
|
+
return this.primary();
|
|
229
|
+
}
|
|
230
|
+
primary() {
|
|
231
|
+
this.skip();
|
|
232
|
+
if (this.i >= this.s.length) return null;
|
|
233
|
+
if (this.eat("(")) {
|
|
234
|
+
const e = this.or();
|
|
235
|
+
return e && this.eat(")") ? e : null;
|
|
236
|
+
}
|
|
237
|
+
const s = this.s;
|
|
238
|
+
const c = s[this.i];
|
|
239
|
+
if (isDigit(c)) {
|
|
240
|
+
const start3 = this.i;
|
|
241
|
+
while (isDigit(s[this.i])) this.i++;
|
|
242
|
+
if (s[this.i] === "." && isDigit(s[this.i + 1])) {
|
|
243
|
+
this.i++;
|
|
244
|
+
while (isDigit(s[this.i])) this.i++;
|
|
245
|
+
}
|
|
246
|
+
return { k: "lit", v: Number(s.slice(start3, this.i).join("")) };
|
|
247
|
+
}
|
|
248
|
+
if (!isLetter(c)) return null;
|
|
249
|
+
const start2 = this.i;
|
|
250
|
+
while (isLetter(s[this.i]) || isDigit(s[this.i])) this.i++;
|
|
251
|
+
const name = s.slice(start2, this.i).join("");
|
|
252
|
+
if (name.length > MAX_NAME) return null;
|
|
253
|
+
let j = this.i;
|
|
254
|
+
while (s[j] === " " || s[j] === " ") j++;
|
|
255
|
+
const called = s[j] === "(";
|
|
256
|
+
if (METRIC_NAMES.includes(name)) {
|
|
257
|
+
if (!called) return { k: "metric", metric: name, target: "" };
|
|
258
|
+
this.eat("(");
|
|
259
|
+
const from = this.i;
|
|
260
|
+
while (this.i < s.length && s[this.i] !== ")" && s[this.i] !== "(") this.i++;
|
|
261
|
+
if (s[this.i] !== ")") return null;
|
|
262
|
+
const target = s.slice(from, this.i).join("").replace(/^[ \t]+|[ \t]+$/g, "");
|
|
263
|
+
this.i++;
|
|
264
|
+
return { k: "metric", metric: name, target };
|
|
265
|
+
}
|
|
266
|
+
if (name in FUNCTIONS) {
|
|
267
|
+
if (!called || !this.eat("(")) return null;
|
|
268
|
+
const args = [];
|
|
269
|
+
if (!this.eat(")")) {
|
|
270
|
+
do {
|
|
271
|
+
const a = this.or();
|
|
272
|
+
if (!a) return null;
|
|
273
|
+
args.push(a);
|
|
274
|
+
} while (this.eat(","));
|
|
275
|
+
if (!this.eat(")")) return null;
|
|
276
|
+
}
|
|
277
|
+
const arity = FUNCTIONS[name];
|
|
278
|
+
if (arity !== null ? args.length !== arity : args.length === 0) return null;
|
|
279
|
+
return { k: "call", fn: name, args };
|
|
280
|
+
}
|
|
281
|
+
if (called) return null;
|
|
282
|
+
if (name === "true") return { k: "lit", v: true };
|
|
283
|
+
if (name === "false") return { k: "lit", v: false };
|
|
284
|
+
return { k: "name", name };
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function describeVar(v) {
|
|
288
|
+
return typeof v === "boolean" ? v ? "true" : "false" : formatTwo(v);
|
|
289
|
+
}
|
|
290
|
+
function formatTwo(v) {
|
|
291
|
+
const n = Math.floor(Math.abs(v) * 100 + 0.5);
|
|
292
|
+
if (!(n < 1e15)) return v < 0 ? "-\u221E" : "\u221E";
|
|
293
|
+
const whole = Math.floor(n / 100), part = n - whole * 100;
|
|
294
|
+
let s = String(whole);
|
|
295
|
+
if (part > 0) s += "." + String(part).padStart(2, "0").replace(/0+$/, "");
|
|
296
|
+
return (v < 0 && n > 0 ? "-" : "") + s;
|
|
297
|
+
}
|
|
298
|
+
function filling(text, values) {
|
|
299
|
+
if (!text.includes("{")) return text;
|
|
300
|
+
let out = "", rest = text;
|
|
301
|
+
for (let open = rest.indexOf("{"); open >= 0; open = rest.indexOf("{")) {
|
|
302
|
+
out += rest.slice(0, open);
|
|
303
|
+
const close = rest.indexOf("}", open);
|
|
304
|
+
if (close < 0) {
|
|
305
|
+
out += rest.slice(open);
|
|
306
|
+
rest = "";
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
const inner = rest.slice(open + 1, close);
|
|
310
|
+
const e = inner.includes("{") ? null : parseExpr(inner);
|
|
311
|
+
const names = e ? exprNames(e) : [];
|
|
312
|
+
if (e && exprMetrics(e).length === 0 && names.length > 0 && names.every((n) => values[n] !== void 0)) out += describeVar(evaluate(e, values));
|
|
313
|
+
else out += rest.slice(open, close + 1);
|
|
314
|
+
rest = rest.slice(close + 1);
|
|
315
|
+
}
|
|
316
|
+
return out + rest;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/state.ts
|
|
320
|
+
var MAX_NAME2 = 64;
|
|
321
|
+
function isName(name) {
|
|
322
|
+
return name.length <= MAX_NAME2 && /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
|
|
323
|
+
}
|
|
324
|
+
function mentionsMetric(condition) {
|
|
325
|
+
const e = parseExpr(condition);
|
|
326
|
+
return e !== null && exprMetrics(e).length > 0;
|
|
327
|
+
}
|
|
66
328
|
function holds(condition, state, metric = null) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (m !== null) {
|
|
71
|
-
const v = metric ? metric(m) : null;
|
|
72
|
-
return v !== null && passes(m, v);
|
|
73
|
-
}
|
|
74
|
-
let term = trim(all);
|
|
75
|
-
let negated = false;
|
|
76
|
-
if (term.startsWith("!")) {
|
|
77
|
-
negated = true;
|
|
78
|
-
term = trim(term.slice(1));
|
|
79
|
-
}
|
|
80
|
-
return (state[term] ?? false) !== negated;
|
|
81
|
-
}));
|
|
329
|
+
const e = parseExpr(condition);
|
|
330
|
+
if (!e) return false;
|
|
331
|
+
return truthy(evaluate(e, state, metric ? (m, target) => metric({ metric: m, target, op: "", value: 0 }) : null));
|
|
82
332
|
}
|
|
83
333
|
function stateValues(page, choice) {
|
|
84
334
|
if (choice === "all") return null;
|
|
@@ -117,7 +367,7 @@ function applyOverride(o, e) {
|
|
|
117
367
|
return out;
|
|
118
368
|
}
|
|
119
369
|
function changesState(c) {
|
|
120
|
-
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0);
|
|
370
|
+
return !!c && (c.reset === true || Object.keys(c.set ?? {}).length > 0 || (c.toggle ?? []).length > 0 || !!c.run);
|
|
121
371
|
}
|
|
122
372
|
function applyClick(c, state, initial = {}) {
|
|
123
373
|
let out = { ...state };
|
|
@@ -126,27 +376,91 @@ function applyClick(c, state, initial = {}) {
|
|
|
126
376
|
for (const k of Object.keys(state)) out[k] = false;
|
|
127
377
|
Object.assign(out, initial);
|
|
128
378
|
}
|
|
129
|
-
|
|
130
|
-
for (const k of c.
|
|
379
|
+
const before = { ...out };
|
|
380
|
+
for (const [k, v] of Object.entries(c.set ?? {})) out[k] = clickValue(v, before, before[k]);
|
|
381
|
+
for (const k of c.toggle ?? []) out[k] = !truthy(out[k] ?? false);
|
|
131
382
|
return out;
|
|
132
383
|
}
|
|
133
|
-
function
|
|
134
|
-
|
|
384
|
+
function clickValue(v, state, current) {
|
|
385
|
+
if (typeof v !== "string") return v;
|
|
386
|
+
const e = parseExpr(v);
|
|
387
|
+
return e ? evaluate(e, state) : current ?? false;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// src/theme.ts
|
|
391
|
+
var rgb = (r, g, b) => ({ r, g, b, a: 1 });
|
|
392
|
+
function hex(h) {
|
|
393
|
+
const n = parseInt(h.slice(1), 16);
|
|
394
|
+
return { r: (n >> 16 & 255) / 255, g: (n >> 8 & 255) / 255, b: (n & 255) / 255, a: 1 };
|
|
395
|
+
}
|
|
396
|
+
var PALETTE_NAMES = ["accent", "muted", "danger", "warning", "success", "info"];
|
|
397
|
+
var palette = (colors) => PALETTE_NAMES.map((name, i) => ({ name, color: hex(colors[i]) }));
|
|
398
|
+
var LIGHT_PALETTE = palette(["#2563eb", "#6b7280", "#dc2626", "#d97706", "#16a34a", "#0891b2"]);
|
|
399
|
+
var DARK_PALETTE = palette(["#93c5fd", "#cbd5e1", "#fca5a5", "#fcd34d", "#86efac", "#67e8f9"]);
|
|
400
|
+
var themes = [
|
|
401
|
+
{ id: "paper", name: "Paper", background: rgb(0.984, 0.98, 0.965), ink: rgb(0.12, 0.12, 0.13), palette: LIGHT_PALETTE },
|
|
402
|
+
{ id: "white", name: "White", background: rgb(1, 1, 1), ink: rgb(0.12, 0.12, 0.13), palette: LIGHT_PALETTE },
|
|
403
|
+
{
|
|
404
|
+
id: "sepia",
|
|
405
|
+
name: "Sepia",
|
|
406
|
+
background: rgb(0.953, 0.914, 0.824),
|
|
407
|
+
ink: rgb(0.23, 0.18, 0.13),
|
|
408
|
+
palette: palette(["#9a3412", "#78716c", "#b91c1c", "#b45309", "#4d7c0f", "#0e7490"])
|
|
409
|
+
},
|
|
410
|
+
{ id: "blueprint", name: "Blueprint", background: rgb(0.106, 0.227, 0.361), ink: rgb(0.91, 0.945, 0.984), palette: DARK_PALETTE },
|
|
411
|
+
{ id: "chalkboard", name: "Chalkboard", background: rgb(0.169, 0.227, 0.2), ink: rgb(0.945, 0.945, 0.918), palette: DARK_PALETTE },
|
|
412
|
+
{ id: "midnight", name: "Midnight", background: rgb(0.082, 0.086, 0.102), ink: rgb(0.925, 0.925, 0.94), palette: DARK_PALETTE }
|
|
413
|
+
];
|
|
414
|
+
function themeNamed(id) {
|
|
415
|
+
return themes.find((t) => t.id === id) ?? null;
|
|
416
|
+
}
|
|
417
|
+
function pageTheme(page) {
|
|
418
|
+
const c = page.customTheme;
|
|
419
|
+
if (c) return { id: "custom", name: c.name, background: c.background, ink: c.ink, palette: c.palette };
|
|
420
|
+
return themeNamed(page.theme);
|
|
421
|
+
}
|
|
422
|
+
function paletteColor(page, name) {
|
|
423
|
+
const n = name.trim().toLowerCase();
|
|
424
|
+
return (pageTheme(page)?.palette ?? LIGHT_PALETTE).find((p) => p.name.toLowerCase() === n)?.color ?? null;
|
|
425
|
+
}
|
|
426
|
+
function resolvingPalette(page) {
|
|
427
|
+
const named = (e) => !!e.style.named;
|
|
428
|
+
if (!page.elements.some(named) && !(page.symbols ?? []).some((s) => s.elements.some(named))) return page;
|
|
429
|
+
const resolve2 = (e) => {
|
|
430
|
+
const n = e.style.named;
|
|
431
|
+
if (!n) return e;
|
|
432
|
+
const style2 = { ...e.style };
|
|
433
|
+
const f = n.fill !== null ? paletteColor(page, n.fill) : null;
|
|
434
|
+
const s = n.stroke !== null ? paletteColor(page, n.stroke) : null;
|
|
435
|
+
const t = n.textColor !== null ? paletteColor(page, n.textColor) : null;
|
|
436
|
+
if (f) style2.fill = { ...f };
|
|
437
|
+
if (s) style2.stroke = { ...s };
|
|
438
|
+
if (t) style2.textColor = { ...t };
|
|
439
|
+
return { ...e, style: style2 };
|
|
440
|
+
};
|
|
441
|
+
return {
|
|
442
|
+
...page,
|
|
443
|
+
elements: page.elements.map(resolve2),
|
|
444
|
+
symbols: page.symbols ? page.symbols.map((s) => ({ ...s, elements: s.elements.map(resolve2) })) : page.symbols
|
|
445
|
+
};
|
|
135
446
|
}
|
|
136
447
|
|
|
137
448
|
// src/model.ts
|
|
138
|
-
var SUPPORTED_VERSION =
|
|
449
|
+
var SUPPORTED_VERSION = 3;
|
|
139
450
|
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
140
451
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
141
452
|
var KNOWN_FIELDS = {
|
|
142
453
|
file: ["version", "pages", "images"],
|
|
143
|
-
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
454
|
+
page: ["id", "name", "layers", "elements", "theme", "customTheme", "logic", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols", "variables", "flows"],
|
|
144
455
|
flow: ["id", "from", "to", "every", "speed", "color", "size", "when", "spread", "volume", "overrides"],
|
|
145
456
|
flowOverride: ["when", "volume"],
|
|
146
457
|
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
147
458
|
layer: ["id", "name", "visible", "locked"],
|
|
148
459
|
rgba: ["r", "g", "b", "a"],
|
|
149
|
-
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor"],
|
|
460
|
+
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "font", "gridSize", "opacity", "dashOffset", "textColor", "named"],
|
|
461
|
+
colorNames: ["fill", "stroke", "textColor"],
|
|
462
|
+
customTheme: ["name", "background", "ink", "palette", "base"],
|
|
463
|
+
paletteColor: ["name", "color"],
|
|
150
464
|
element: [
|
|
151
465
|
"id",
|
|
152
466
|
"kind",
|
|
@@ -184,8 +498,11 @@ var KNOWN_FIELDS = {
|
|
|
184
498
|
"capacity",
|
|
185
499
|
"delay",
|
|
186
500
|
"queue",
|
|
187
|
-
"gauge"
|
|
501
|
+
"gauge",
|
|
502
|
+
"containerID",
|
|
503
|
+
"container"
|
|
188
504
|
],
|
|
505
|
+
container: ["fixed", "lanes"],
|
|
189
506
|
gauge: ["metric", "of", "max", "warn", "alarm", "style", "label"],
|
|
190
507
|
strokePoint: ["x", "y"],
|
|
191
508
|
subpath: ["closed", "coords"],
|
|
@@ -194,7 +511,7 @@ var KNOWN_FIELDS = {
|
|
|
194
511
|
extensionRef: ["id", "shape", "version", "params"],
|
|
195
512
|
normalizedPoint: ["x", "y"],
|
|
196
513
|
normalizedRect: ["x", "y", "width", "height"],
|
|
197
|
-
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops", "cost"],
|
|
514
|
+
connector: ["start", "end", "routing", "startHead", "endHead", "waypoints", "hops", "avoid", "cost"],
|
|
198
515
|
waypoint: ["x", "y"],
|
|
199
516
|
timeline: ["fps", "duration", "boil", "settle", "poster", "tracks", "camera", "markers"],
|
|
200
517
|
track: ["element", "keys", "boil", "follow", "repeats", "march"],
|
|
@@ -203,7 +520,7 @@ var KNOWN_FIELDS = {
|
|
|
203
520
|
repeat: ["start", "end", "count"],
|
|
204
521
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
205
522
|
marker: ["t", "name", "loop"],
|
|
206
|
-
click: ["scene", "url", "set", "toggle", "reset"],
|
|
523
|
+
click: ["scene", "url", "set", "toggle", "reset", "run"],
|
|
207
524
|
hover: ["stroke", "fill", "opacity"],
|
|
208
525
|
override: ["when", "stroke", "fill", "textColor", "opacity", "strokeWidth", "dash", "text", "reroute", "offline", "cost", "capacity", "delay"]
|
|
209
526
|
};
|
|
@@ -217,7 +534,7 @@ var inkOnDark = { r: 0.93, g: 0.93, b: 0.94, a: 1 };
|
|
|
217
534
|
var DottieFormatError = class extends Error {
|
|
218
535
|
};
|
|
219
536
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
220
|
-
var
|
|
537
|
+
var num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
221
538
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
222
539
|
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
223
540
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
@@ -241,9 +558,9 @@ function webLink(url) {
|
|
|
241
558
|
}
|
|
242
559
|
function rgba(v) {
|
|
243
560
|
if (!isObj(v)) return void 0;
|
|
244
|
-
const r =
|
|
561
|
+
const r = num2(v.r), g = num2(v.g), b = num2(v.b);
|
|
245
562
|
if (r === void 0 || g === void 0 || b === void 0) return void 0;
|
|
246
|
-
return { r, g, b, a:
|
|
563
|
+
return { r, g, b, a: num2(v.a) ?? 1 };
|
|
247
564
|
}
|
|
248
565
|
var Reader = class {
|
|
249
566
|
constructor() {
|
|
@@ -272,28 +589,55 @@ var Reader = class {
|
|
|
272
589
|
stroke: rgba(s.stroke) ?? { ...black },
|
|
273
590
|
fill: rgba(s.fill) ?? { ...clear },
|
|
274
591
|
fillStyle: ["solid", "hachure", "crossHatch", "grid"].includes(fillStyle) ? fillStyle : "solid",
|
|
275
|
-
strokeWidth:
|
|
592
|
+
strokeWidth: num2(s.strokeWidth) ?? 2,
|
|
276
593
|
dash: ["solid", "dashed", "dotted"].includes(dash) ? dash : "solid",
|
|
277
|
-
roughness:
|
|
278
|
-
bowing:
|
|
279
|
-
fontSize:
|
|
594
|
+
roughness: num2(s.roughness) ?? 1,
|
|
595
|
+
bowing: num2(s.bowing) ?? 1,
|
|
596
|
+
fontSize: num2(s.fontSize) ?? 18,
|
|
280
597
|
font: str(s.font) || null,
|
|
281
|
-
gridSize:
|
|
282
|
-
opacity:
|
|
283
|
-
dashOffset:
|
|
284
|
-
textColor: rgba(s.textColor) ?? null
|
|
598
|
+
gridSize: num2(s.gridSize) ?? null,
|
|
599
|
+
opacity: num2(s.opacity) ?? null,
|
|
600
|
+
dashOffset: num2(s.dashOffset) ?? null,
|
|
601
|
+
textColor: rgba(s.textColor) ?? null,
|
|
602
|
+
named: this.colorNames(s.named)
|
|
285
603
|
};
|
|
286
604
|
}
|
|
605
|
+
container(v) {
|
|
606
|
+
if (!isObj(v)) return null;
|
|
607
|
+
this.fields(v, "container");
|
|
608
|
+
return { fixed: v.fixed === true ? true : null, lanes: v.lanes === "rows" || v.lanes === "columns" ? v.lanes : null };
|
|
609
|
+
}
|
|
610
|
+
colorNames(v) {
|
|
611
|
+
if (!isObj(v)) return null;
|
|
612
|
+
this.fields(v, "colorNames");
|
|
613
|
+
const n = { fill: str(v.fill) ?? null, stroke: str(v.stroke) ?? null, textColor: str(v.textColor) ?? null };
|
|
614
|
+
return n.fill === null && n.stroke === null && n.textColor === null ? null : n;
|
|
615
|
+
}
|
|
616
|
+
customTheme(v) {
|
|
617
|
+
if (!isObj(v)) return null;
|
|
618
|
+
this.fields(v, "customTheme");
|
|
619
|
+
const background = rgba(v.background), ink = rgba(v.ink);
|
|
620
|
+
if (!background || !ink) {
|
|
621
|
+
this.warn("A custom theme needs a background and an ink; drawn without it");
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
const palette2 = (Array.isArray(v.palette) ? v.palette : []).filter(isObj).flatMap((p) => {
|
|
625
|
+
this.fields(p, "paletteColor");
|
|
626
|
+
const name = str(p.name), color2 = rgba(p.color);
|
|
627
|
+
return name && color2 ? [{ name, color: color2 }] : [];
|
|
628
|
+
});
|
|
629
|
+
return { name: str(v.name) ?? "Custom", background, ink, palette: palette2, base: str(v.base) ?? null };
|
|
630
|
+
}
|
|
287
631
|
endpoint(v) {
|
|
288
632
|
const e = isObj(v) ? v : {};
|
|
289
633
|
const port = str(e.port);
|
|
290
|
-
const portIndex =
|
|
634
|
+
const portIndex = num2(e.portIndex);
|
|
291
635
|
return {
|
|
292
636
|
elementID: uuid(e.elementID) ?? null,
|
|
293
637
|
port: port && ["top", "right", "bottom", "left"].includes(port) ? port : null,
|
|
294
638
|
portIndex: portIndex !== void 0 && Number.isInteger(portIndex) && portIndex >= 0 ? portIndex : null,
|
|
295
|
-
x:
|
|
296
|
-
y:
|
|
639
|
+
x: num2(e.x) ?? 0,
|
|
640
|
+
y: num2(e.y) ?? 0
|
|
297
641
|
};
|
|
298
642
|
}
|
|
299
643
|
connector(v) {
|
|
@@ -312,9 +656,10 @@ var Reader = class {
|
|
|
312
656
|
endHead: head(v.endHead, "arrow"),
|
|
313
657
|
waypoints: Array.isArray(v.waypoints) ? v.waypoints.filter(isObj).map((w) => {
|
|
314
658
|
this.fields(w, "waypoint");
|
|
315
|
-
return { x:
|
|
659
|
+
return { x: num2(w.x) ?? 0, y: num2(w.y) ?? 0 };
|
|
316
660
|
}) : null,
|
|
317
661
|
hops: typeof v.hops === "boolean" ? v.hops : null,
|
|
662
|
+
avoid: typeof v.avoid === "boolean" ? v.avoid : null,
|
|
318
663
|
cost: typeof v.cost === "number" && Number.isFinite(v.cost) ? v.cost : null
|
|
319
664
|
};
|
|
320
665
|
}
|
|
@@ -347,20 +692,20 @@ var Reader = class {
|
|
|
347
692
|
id,
|
|
348
693
|
kind,
|
|
349
694
|
layerID,
|
|
350
|
-
x:
|
|
351
|
-
y:
|
|
352
|
-
width:
|
|
353
|
-
height:
|
|
695
|
+
x: num2(v.x) ?? 0,
|
|
696
|
+
y: num2(v.y) ?? 0,
|
|
697
|
+
width: num2(v.width) ?? 0,
|
|
698
|
+
height: num2(v.height) ?? 0,
|
|
354
699
|
style: this.style(v.style),
|
|
355
|
-
seed: (
|
|
700
|
+
seed: (num2(v.seed) ?? 1) >>> 0,
|
|
356
701
|
text: str(v.text) ?? null,
|
|
357
702
|
connector: this.connector(v.connector),
|
|
358
703
|
groupID: uuid(v.groupID) ?? null,
|
|
359
|
-
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x:
|
|
704
|
+
points: Array.isArray(v.points) ? v.points.filter(isObj).map((p) => ({ x: num2(p.x) ?? 0, y: num2(p.y) ?? 0 })) : null,
|
|
360
705
|
path: Array.isArray(v.path) ? v.path.filter((c) => typeof c === "number") : null,
|
|
361
706
|
subpaths,
|
|
362
707
|
evenOdd: bool(v.evenOdd) ?? null,
|
|
363
|
-
rotation:
|
|
708
|
+
rotation: num2(v.rotation) ?? null,
|
|
364
709
|
link: uuid(v.link) ?? null,
|
|
365
710
|
url: str(v.url) ?? null,
|
|
366
711
|
anchor: str(v.anchor) ?? null,
|
|
@@ -370,20 +715,22 @@ var Reader = class {
|
|
|
370
715
|
overrides: this.overrides(v.overrides),
|
|
371
716
|
symbol: uuid(v.symbol) ?? null,
|
|
372
717
|
playback: this.playback(v.playback),
|
|
373
|
-
offset:
|
|
718
|
+
offset: num2(v.offset) ?? null,
|
|
374
719
|
args: strings(v.args),
|
|
375
720
|
image: this.image(v.image, images),
|
|
376
|
-
reveal:
|
|
721
|
+
reveal: num2(v.reveal) ?? null,
|
|
377
722
|
ports: Array.isArray(v.ports) ? v.ports.map((p) => {
|
|
378
723
|
const o = isObj(p) ? p : {};
|
|
379
|
-
return { x:
|
|
724
|
+
return { x: num2(o.x) ?? 0, y: num2(o.y) ?? 0 };
|
|
380
725
|
}) : null,
|
|
381
726
|
labelRect: this.labelRect(v.labelRect),
|
|
382
727
|
extension: this.extension(v.extension),
|
|
383
|
-
capacity:
|
|
384
|
-
delay:
|
|
728
|
+
capacity: num2(v.capacity) ?? null,
|
|
729
|
+
delay: num2(v.delay) ?? null,
|
|
385
730
|
queue: v.queue === true ? true : null,
|
|
386
|
-
gauge: this.gauge(v.gauge)
|
|
731
|
+
gauge: this.gauge(v.gauge),
|
|
732
|
+
containerID: uuid(v.containerID) ?? null,
|
|
733
|
+
container: this.container(v.container)
|
|
387
734
|
};
|
|
388
735
|
}
|
|
389
736
|
gauge(v) {
|
|
@@ -404,7 +751,7 @@ var Reader = class {
|
|
|
404
751
|
labelRect(v) {
|
|
405
752
|
if (!isObj(v)) return null;
|
|
406
753
|
this.fields(v, "normalizedRect");
|
|
407
|
-
return { x:
|
|
754
|
+
return { x: num2(v.x) ?? 0, y: num2(v.y) ?? 0, width: num2(v.width) ?? 0, height: num2(v.height) ?? 0 };
|
|
408
755
|
}
|
|
409
756
|
extension(v) {
|
|
410
757
|
if (!isObj(v)) return null;
|
|
@@ -425,7 +772,7 @@ var Reader = class {
|
|
|
425
772
|
variables(v) {
|
|
426
773
|
if (!isObj(v)) return null;
|
|
427
774
|
const out = {};
|
|
428
|
-
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean") out[k] = b;
|
|
775
|
+
for (const [k, b] of Object.entries(v)) if (typeof b === "boolean" || typeof b === "number" && Number.isFinite(b)) out[k] = b;
|
|
429
776
|
return Object.keys(out).length > 0 ? out : null;
|
|
430
777
|
}
|
|
431
778
|
click(v) {
|
|
@@ -437,12 +784,22 @@ var Reader = class {
|
|
|
437
784
|
let set = null;
|
|
438
785
|
if (isObj(v.set)) {
|
|
439
786
|
set = {};
|
|
440
|
-
for (const [k, b] of Object.entries(v.set))
|
|
787
|
+
for (const [k, b] of Object.entries(v.set)) {
|
|
788
|
+
if (isName(k) && (typeof b === "boolean" || typeof b === "string" || typeof b === "number" && Number.isFinite(b))) set[k] = b;
|
|
789
|
+
}
|
|
441
790
|
if (Object.keys(set).length === 0) set = null;
|
|
442
791
|
}
|
|
443
792
|
const raw = typeof v.toggle === "string" ? [v.toggle] : Array.isArray(v.toggle) && v.toggle.every((x) => typeof x === "string") ? v.toggle : null;
|
|
444
793
|
const toggle = raw ? raw.filter(isName) : null;
|
|
445
|
-
|
|
794
|
+
const run = str(v.run);
|
|
795
|
+
return {
|
|
796
|
+
scene: str(v.scene) ?? null,
|
|
797
|
+
url: safe,
|
|
798
|
+
set,
|
|
799
|
+
toggle: toggle && toggle.length > 0 ? toggle : null,
|
|
800
|
+
reset: v.reset === true ? true : null,
|
|
801
|
+
run: run !== void 0 && isName(run) ? run : null
|
|
802
|
+
};
|
|
446
803
|
}
|
|
447
804
|
playback(v) {
|
|
448
805
|
const s = str(v);
|
|
@@ -458,7 +815,7 @@ var Reader = class {
|
|
|
458
815
|
return null;
|
|
459
816
|
}
|
|
460
817
|
const size = (x) => {
|
|
461
|
-
const n =
|
|
818
|
+
const n = num2(x);
|
|
462
819
|
return Math.max(1, n ?? 100);
|
|
463
820
|
};
|
|
464
821
|
const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
|
|
@@ -495,8 +852,8 @@ var Reader = class {
|
|
|
495
852
|
stroke: rgba(o.stroke) ?? null,
|
|
496
853
|
fill: rgba(o.fill) ?? null,
|
|
497
854
|
textColor: rgba(o.textColor) ?? null,
|
|
498
|
-
opacity:
|
|
499
|
-
strokeWidth:
|
|
855
|
+
opacity: num2(o.opacity) ?? null,
|
|
856
|
+
strokeWidth: num2(o.strokeWidth) ?? null,
|
|
500
857
|
dash: dash !== void 0 && ["solid", "dashed", "dotted"].includes(dash) ? dash : null,
|
|
501
858
|
text: str(o.text) ?? null,
|
|
502
859
|
reroute,
|
|
@@ -510,7 +867,7 @@ var Reader = class {
|
|
|
510
867
|
hover(v) {
|
|
511
868
|
if (!isObj(v)) return null;
|
|
512
869
|
this.fields(v, "hover");
|
|
513
|
-
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity:
|
|
870
|
+
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num2(v.opacity) ?? null };
|
|
514
871
|
}
|
|
515
872
|
ease(v) {
|
|
516
873
|
const s = str(v);
|
|
@@ -524,11 +881,11 @@ var Reader = class {
|
|
|
524
881
|
keyframe(v) {
|
|
525
882
|
if (!isObj(v)) return null;
|
|
526
883
|
this.fields(v, "keyframe");
|
|
527
|
-
const t =
|
|
884
|
+
const t = num2(v.t);
|
|
528
885
|
if (t === void 0) return null;
|
|
529
886
|
const k = { t };
|
|
530
887
|
for (const p of ["x", "y", "width", "height", "rotation", "strokeWidth", "reveal", "opacity"]) {
|
|
531
|
-
const n =
|
|
888
|
+
const n = num2(v[p]);
|
|
532
889
|
if (n !== void 0) k[p] = n;
|
|
533
890
|
}
|
|
534
891
|
const stroke = rgba(v.stroke), fill = rgba(v.fill), visible = bool(v.visible), ease = this.ease(v.ease);
|
|
@@ -546,39 +903,39 @@ var Reader = class {
|
|
|
546
903
|
const keys = (Array.isArray(v.keys) ? v.keys : []).map((k) => this.keyframe(k)).filter((k) => k !== null && k.t >= 0 && k.t <= MAX_DURATION);
|
|
547
904
|
stableSortBy(keys, (k) => k.t);
|
|
548
905
|
const follow = Array.isArray(v.follow) ? v.follow.filter(isObj).flatMap((f) => {
|
|
549
|
-
const path = uuid(f.path), start2 =
|
|
906
|
+
const path = uuid(f.path), start2 = num2(f.start), end = num2(f.end);
|
|
550
907
|
if (!path || start2 === void 0 || end === void 0 || start2 < 0 || end > MAX_DURATION) return [];
|
|
551
|
-
return [{ path, start: start2, end, ease: this.ease(f.ease), reverse: bool(f.reverse) ?? null, orient: bool(f.orient) ?? null, laps:
|
|
908
|
+
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 }];
|
|
552
909
|
}) : null;
|
|
553
910
|
const repeats = Array.isArray(v.repeats) ? v.repeats.filter(isObj).flatMap((r) => {
|
|
554
|
-
const start2 =
|
|
911
|
+
const start2 = num2(r.start), end = num2(r.end);
|
|
555
912
|
if (start2 === void 0 || end === void 0) return [];
|
|
556
|
-
const count =
|
|
913
|
+
const count = num2(r.count);
|
|
557
914
|
const rep = { start: start2, end, count: count === void 0 ? null : Math.trunc(count) };
|
|
558
915
|
return repeatIsValid(rep) ? [rep] : [];
|
|
559
916
|
}) : null;
|
|
560
|
-
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(
|
|
917
|
+
return { element, keys, boil: bool(v.boil) ?? null, follow, repeats, march: clampMarch(num2(v.march)) };
|
|
561
918
|
}
|
|
562
919
|
timeline(v) {
|
|
563
920
|
if (!isObj(v)) return null;
|
|
564
921
|
this.fields(v, "timeline");
|
|
565
|
-
const boil =
|
|
922
|
+
const boil = num2(v.boil);
|
|
566
923
|
const camera = Array.isArray(v.camera) ? v.camera.filter(isObj).flatMap((c) => {
|
|
567
|
-
const t =
|
|
924
|
+
const t = num2(c.t), x = num2(c.x), y = num2(c.y), width = num2(c.width), height = num2(c.height);
|
|
568
925
|
if (t === void 0 || x === void 0 || y === void 0 || width === void 0 || height === void 0) return [];
|
|
569
926
|
if (!(width > 1 && height > 1 && t >= 0)) return [];
|
|
570
927
|
return [{ t, x, y, width, height, ease: this.ease(c.ease) }];
|
|
571
928
|
}) : null;
|
|
572
929
|
if (camera) stableSortBy(camera, (c) => c.t);
|
|
573
930
|
const markers = Array.isArray(v.markers) ? v.markers.filter(isObj).flatMap((m) => {
|
|
574
|
-
const t =
|
|
931
|
+
const t = num2(m.t);
|
|
575
932
|
return t !== void 0 && t >= 0 ? [{ t, name: str(m.name) ?? "", loop: bool(m.loop) ?? null }] : [];
|
|
576
933
|
}) : null;
|
|
577
934
|
if (markers) stableSortBy(markers, (m) => m.t);
|
|
578
|
-
const poster =
|
|
935
|
+
const poster = num2(v.poster);
|
|
579
936
|
return {
|
|
580
|
-
fps: clampFPS(
|
|
581
|
-
duration: clampTime(
|
|
937
|
+
fps: clampFPS(num2(v.fps) ?? 12),
|
|
938
|
+
duration: clampTime(num2(v.duration) ?? 0),
|
|
582
939
|
boil: boil !== void 0 && Number.isInteger(boil) && boil > 0 ? Math.min(boil, 1e4) : null,
|
|
583
940
|
settle: bool(v.settle) ?? null,
|
|
584
941
|
poster: poster === void 0 ? null : clampTime(poster),
|
|
@@ -604,6 +961,8 @@ var Reader = class {
|
|
|
604
961
|
layers,
|
|
605
962
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
606
963
|
theme: str(v.theme) ?? null,
|
|
964
|
+
customTheme: this.customTheme(v.customTheme),
|
|
965
|
+
logic: typeof v.logic === "string" && v.logic.length > 0 ? v.logic : null,
|
|
607
966
|
timeline: this.timeline(v.timeline),
|
|
608
967
|
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null,
|
|
609
968
|
variables: this.variables(v.variables),
|
|
@@ -615,7 +974,7 @@ var Reader = class {
|
|
|
615
974
|
this.fields(f, "flow");
|
|
616
975
|
const id = (x) => typeof x === "string" && UUID.test(x) ? x.toUpperCase() : NO_ID;
|
|
617
976
|
const finite = (x) => {
|
|
618
|
-
const n =
|
|
977
|
+
const n = num2(x);
|
|
619
978
|
return n !== void 0 && Number.isFinite(n) ? n : null;
|
|
620
979
|
};
|
|
621
980
|
return {
|
|
@@ -655,7 +1014,7 @@ function parseDottie(source) {
|
|
|
655
1014
|
}
|
|
656
1015
|
}
|
|
657
1016
|
if (!isObj(json)) throw new DottieFormatError("Not a Dottie drawing");
|
|
658
|
-
const version =
|
|
1017
|
+
const version = num2(json.version);
|
|
659
1018
|
if (version === void 0) throw new DottieFormatError("Not a Dottie drawing (no version)");
|
|
660
1019
|
if (!Array.isArray(json.pages) || json.pages.length === 0) throw new DottieFormatError("A drawing needs at least one page");
|
|
661
1020
|
const reader = new Reader();
|
|
@@ -667,7 +1026,7 @@ function parseDottie(source) {
|
|
|
667
1026
|
if (isObj(v) && typeof v.data === "string") images[k] = reader.picture(v.data, str(v.format));
|
|
668
1027
|
}
|
|
669
1028
|
}
|
|
670
|
-
const pages = json.pages.map((p, i) => reader.page(p, images, i + 1));
|
|
1029
|
+
const pages = json.pages.map((p, i) => resolvingPalette(reader.page(p, images, i + 1)));
|
|
671
1030
|
if (new Set(pages.map((p) => p.id)).size !== pages.length) throw new DottieFormatError("Two pages share an id");
|
|
672
1031
|
return { version, pages, warnings: reader.warnings };
|
|
673
1032
|
}
|
|
@@ -687,7 +1046,8 @@ function cloneElement(e) {
|
|
|
687
1046
|
...e.style,
|
|
688
1047
|
stroke: { ...e.style.stroke },
|
|
689
1048
|
fill: { ...e.style.fill },
|
|
690
|
-
textColor: e.style.textColor ? { ...e.style.textColor } : null
|
|
1049
|
+
textColor: e.style.textColor ? { ...e.style.textColor } : null,
|
|
1050
|
+
named: e.style.named ? { ...e.style.named } : e.style.named
|
|
691
1051
|
},
|
|
692
1052
|
connector: e.connector ? {
|
|
693
1053
|
...e.connector,
|
|
@@ -1177,6 +1537,165 @@ function shapePath(e) {
|
|
|
1177
1537
|
return path;
|
|
1178
1538
|
}
|
|
1179
1539
|
|
|
1540
|
+
// src/elbow.ts
|
|
1541
|
+
var MARGIN = 16;
|
|
1542
|
+
var BEND = 24;
|
|
1543
|
+
var SMALLEST = 24;
|
|
1544
|
+
var MOST = 48;
|
|
1545
|
+
function obstacle(e) {
|
|
1546
|
+
if (e.kind === "connector" || e.kind === "text" || e.kind === "freehand") return null;
|
|
1547
|
+
if (e.kind === "path" && !e.extension) return null;
|
|
1548
|
+
if (e.style.opacity !== null && e.style.opacity <= 0) return null;
|
|
1549
|
+
const r = rotatedBounds(e);
|
|
1550
|
+
return r.width < SMALLEST || r.height < SMALLEST ? null : r;
|
|
1551
|
+
}
|
|
1552
|
+
function crosses(points, r) {
|
|
1553
|
+
const x1 = r.x + r.width, y1 = r.y + r.height;
|
|
1554
|
+
for (let k = 1; k < points.length; k++) {
|
|
1555
|
+
const p = points[k - 1], q = points[k];
|
|
1556
|
+
if (p.y === q.y) {
|
|
1557
|
+
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;
|
|
1558
|
+
} else if (p.x === q.x) {
|
|
1559
|
+
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;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return false;
|
|
1563
|
+
}
|
|
1564
|
+
var padded = (r) => ({ x: r.x - MARGIN, y: r.y - MARGIN, width: r.width + 2 * MARGIN, height: r.height + 2 * MARGIN });
|
|
1565
|
+
var inside = (p, r) => r.x < p.x && p.x < r.x + r.width && r.y < p.y && p.y < r.y + r.height;
|
|
1566
|
+
var memo = /* @__PURE__ */ new Map();
|
|
1567
|
+
function avoiding(plain, a, b, startVertical, endVertical, elements, except) {
|
|
1568
|
+
const usable = (r) => !inside(a, padded(r)) && !inside(b, padded(r));
|
|
1569
|
+
const all = [];
|
|
1570
|
+
let crossed = false;
|
|
1571
|
+
for (const e of elements) {
|
|
1572
|
+
if (except.has(e.id)) continue;
|
|
1573
|
+
const r = obstacle(e);
|
|
1574
|
+
if (!r || !usable(r)) continue;
|
|
1575
|
+
all.push(r);
|
|
1576
|
+
if (!crossed && crosses(plain, r)) crossed = true;
|
|
1577
|
+
}
|
|
1578
|
+
if (!crossed) return plain;
|
|
1579
|
+
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])]);
|
|
1580
|
+
const hit = memo.get(key);
|
|
1581
|
+
if (hit) return hit;
|
|
1582
|
+
const out = route(plain, a, b, startVertical, endVertical, all);
|
|
1583
|
+
if (memo.size >= 512) memo.clear();
|
|
1584
|
+
memo.set(key, out);
|
|
1585
|
+
return out;
|
|
1586
|
+
}
|
|
1587
|
+
function route(plain, a, b, startVertical, endVertical, usable) {
|
|
1588
|
+
let chosen = usable.map((_, i) => i).filter((i) => crosses(plain, usable[i]));
|
|
1589
|
+
if (chosen.length === 0) return plain;
|
|
1590
|
+
for (let round = 0; round < 4; round++) {
|
|
1591
|
+
if (chosen.length > MOST) return plain;
|
|
1592
|
+
const path = grid(a, b, startVertical, endVertical, chosen.map((i) => padded(usable[i])));
|
|
1593
|
+
if (!path) return plain;
|
|
1594
|
+
const more = usable.map((_, i) => i).filter((i) => !chosen.includes(i) && crosses(path, usable[i]));
|
|
1595
|
+
if (more.length === 0) return path;
|
|
1596
|
+
chosen = [...chosen, ...more].sort((p, q) => p - q);
|
|
1597
|
+
}
|
|
1598
|
+
return plain;
|
|
1599
|
+
}
|
|
1600
|
+
function grid(a, b, startVertical, endVertical, boxes) {
|
|
1601
|
+
let xs = [a.x, b.x, (a.x + b.x) / 2], ys = [a.y, b.y, (a.y + b.y) / 2];
|
|
1602
|
+
for (const r of boxes) {
|
|
1603
|
+
xs.push(r.x, r.x + r.width);
|
|
1604
|
+
ys.push(r.y, r.y + r.height);
|
|
1605
|
+
}
|
|
1606
|
+
xs = [...new Set(xs)].sort((p, q) => p - q);
|
|
1607
|
+
ys = [...new Set(ys)].sort((p, q) => p - q);
|
|
1608
|
+
const nx = xs.length, ny = ys.length;
|
|
1609
|
+
const ia = xs.indexOf(a.x), ja = ys.indexOf(a.y), ib = xs.indexOf(b.x), jb = ys.indexOf(b.y);
|
|
1610
|
+
if (ia < 0 || ja < 0 || ib < 0 || jb < 0) return null;
|
|
1611
|
+
const at = new Map(xs.map((x, k) => [x, k])), bt = new Map(ys.map((y, k) => [y, k]));
|
|
1612
|
+
const inner = new Uint8Array(nx * ny), acrossBlocked = new Uint8Array(nx * ny), downBlocked = new Uint8Array(nx * ny);
|
|
1613
|
+
for (const r of boxes) {
|
|
1614
|
+
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);
|
|
1615
|
+
if (i0 === void 0 || i1 === void 0 || j0 === void 0 || j1 === void 0) continue;
|
|
1616
|
+
for (let i = i0; i < i1; i++) for (let j = j0; j < j1; j++) {
|
|
1617
|
+
if (j > j0) acrossBlocked[i * ny + j] = 1;
|
|
1618
|
+
if (i > i0) downBlocked[i * ny + j] = 1;
|
|
1619
|
+
if (i > i0 && j > j0) inner[i * ny + j] = 1;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
const blocked = (i, j) => inner[i * ny + j] === 1;
|
|
1623
|
+
const across = (i0, i1, j) => acrossBlocked[Math.min(i0, i1) * ny + j] === 1;
|
|
1624
|
+
const down = (j0, j1, i) => downBlocked[i * ny + Math.min(j0, j1)] === 1;
|
|
1625
|
+
const count = nx * ny * 2;
|
|
1626
|
+
const dist = new Array(count).fill(Infinity), prev = new Array(count).fill(-1);
|
|
1627
|
+
const done = new Array(count).fill(false);
|
|
1628
|
+
const heap = new Frontier();
|
|
1629
|
+
const first = (ia * ny + ja) * 2 + (startVertical ? 1 : 0);
|
|
1630
|
+
dist[first] = 0;
|
|
1631
|
+
heap.push(0, first);
|
|
1632
|
+
const want = (ib * ny + jb) * 2 + (endVertical ? 1 : 0);
|
|
1633
|
+
for (let top = heap.pop(); top; top = heap.pop()) {
|
|
1634
|
+
const [cost, s] = top;
|
|
1635
|
+
if (done[s]) continue;
|
|
1636
|
+
done[s] = true;
|
|
1637
|
+
if (s === want) break;
|
|
1638
|
+
const node = Math.floor(s / 2), axis = s % 2, i = Math.floor(node / ny), j = node % ny;
|
|
1639
|
+
for (const [di, dj] of [[1, 0], [-1, 0], [0, 1], [0, -1]]) {
|
|
1640
|
+
const ni = i + di, nj = j + dj;
|
|
1641
|
+
if (ni < 0 || ni >= nx || nj < 0 || nj >= ny || blocked(ni, nj)) continue;
|
|
1642
|
+
const turn = di !== 0 ? 0 : 1;
|
|
1643
|
+
if (di !== 0 ? across(i, ni, j) : down(j, nj, i)) continue;
|
|
1644
|
+
const length = Math.abs(xs[ni] - xs[i]) + Math.abs(ys[nj] - ys[j]);
|
|
1645
|
+
const c = cost + length + (turn !== axis ? BEND : 0);
|
|
1646
|
+
const t = (ni * ny + nj) * 2 + turn;
|
|
1647
|
+
if (c < dist[t]) {
|
|
1648
|
+
dist[t] = c;
|
|
1649
|
+
prev[t] = s;
|
|
1650
|
+
heap.push(c, t);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
if (!done[want]) return null;
|
|
1655
|
+
const points = [];
|
|
1656
|
+
for (let s = want; s >= 0; s = prev[s]) {
|
|
1657
|
+
const node = Math.floor(s / 2);
|
|
1658
|
+
points.push({ x: xs[Math.floor(node / ny)], y: ys[node % ny] });
|
|
1659
|
+
}
|
|
1660
|
+
return points.reverse();
|
|
1661
|
+
}
|
|
1662
|
+
var Frontier = class {
|
|
1663
|
+
constructor() {
|
|
1664
|
+
__publicField(this, "heap", []);
|
|
1665
|
+
__publicField(this, "next", 0);
|
|
1666
|
+
}
|
|
1667
|
+
less(i, j) {
|
|
1668
|
+
const a = this.heap[i], b = this.heap[j];
|
|
1669
|
+
return a[0] < b[0] || a[0] === b[0] && a[1] < b[1];
|
|
1670
|
+
}
|
|
1671
|
+
push(cost, state) {
|
|
1672
|
+
this.heap.push([cost, this.next++, state]);
|
|
1673
|
+
for (let i = this.heap.length - 1; i > 0; ) {
|
|
1674
|
+
const up = i - 1 >> 1;
|
|
1675
|
+
if (!this.less(i, up)) break;
|
|
1676
|
+
[this.heap[i], this.heap[up]] = [this.heap[up], this.heap[i]];
|
|
1677
|
+
i = up;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
pop() {
|
|
1681
|
+
if (this.heap.length === 0) return null;
|
|
1682
|
+
const top = this.heap[0], last = this.heap.pop();
|
|
1683
|
+
if (this.heap.length > 0) {
|
|
1684
|
+
this.heap[0] = last;
|
|
1685
|
+
for (let i = 0; ; ) {
|
|
1686
|
+
const l = 2 * i + 1, r = l + 1;
|
|
1687
|
+
let m = i;
|
|
1688
|
+
if (l < this.heap.length && this.less(l, m)) m = l;
|
|
1689
|
+
if (r < this.heap.length && this.less(r, m)) m = r;
|
|
1690
|
+
if (m === i) break;
|
|
1691
|
+
[this.heap[i], this.heap[m]] = [this.heap[m], this.heap[i]];
|
|
1692
|
+
i = m;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return [top[0], top[2]];
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1180
1699
|
// src/connector.ts
|
|
1181
1700
|
function portPoint(shape, port) {
|
|
1182
1701
|
const r = frame(shape);
|
|
@@ -1320,7 +1839,7 @@ function routeThrough(p, c) {
|
|
|
1320
1839
|
}
|
|
1321
1840
|
}
|
|
1322
1841
|
}
|
|
1323
|
-
function
|
|
1842
|
+
function route2(e, index) {
|
|
1324
1843
|
const c = e.connector;
|
|
1325
1844
|
if (!c) return { kind: "polyline", points: [] };
|
|
1326
1845
|
const [a, b] = endpoints(e, index);
|
|
@@ -1336,16 +1855,19 @@ function route(e, index) {
|
|
|
1336
1855
|
case "elbow": {
|
|
1337
1856
|
const startV = vertical(c.start.port) ?? !horizontal;
|
|
1338
1857
|
const endV = vertical(c.end.port) ?? !horizontal;
|
|
1858
|
+
let plain;
|
|
1339
1859
|
if (startV && endV) {
|
|
1340
1860
|
const my = a.y + dy / 2;
|
|
1341
|
-
|
|
1342
|
-
}
|
|
1343
|
-
if (!startV && !endV) {
|
|
1861
|
+
plain = [a, { x: a.x, y: my }, { x: b.x, y: my }, b];
|
|
1862
|
+
} else if (!startV && !endV) {
|
|
1344
1863
|
const mx = a.x + dx / 2;
|
|
1345
|
-
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
return { kind: "polyline", points:
|
|
1864
|
+
plain = [a, { x: mx, y: a.y }, { x: mx, y: b.y }, b];
|
|
1865
|
+
} else if (startV) plain = [a, { x: a.x, y: b.y }, b];
|
|
1866
|
+
else plain = [a, { x: b.x, y: a.y }, b];
|
|
1867
|
+
if (c.avoid !== true) return { kind: "polyline", points: plain };
|
|
1868
|
+
const ends = new Set([c.start.elementID, c.end.elementID].filter((x) => x !== null));
|
|
1869
|
+
const routed = avoiding(plain, a, b, startV, endV, index.values(), ends);
|
|
1870
|
+
return { kind: "polyline", points: routed === plain ? plain : simplified(routed) };
|
|
1349
1871
|
}
|
|
1350
1872
|
}
|
|
1351
1873
|
}
|
|
@@ -1356,7 +1878,7 @@ function arrowhead(tip, from, size) {
|
|
|
1356
1878
|
return [wing(spread), wing(-spread)];
|
|
1357
1879
|
}
|
|
1358
1880
|
function connectorBounds(e, index) {
|
|
1359
|
-
const pts = routePoints(
|
|
1881
|
+
const pts = routePoints(route2(e, index));
|
|
1360
1882
|
if (pts.length === 0) return { x: 0, y: 0, width: 0, height: 0 };
|
|
1361
1883
|
let x0 = pts[0].x, y0 = pts[0].y, x1 = x0, y1 = y0;
|
|
1362
1884
|
for (const p of pts) {
|
|
@@ -1368,7 +1890,7 @@ function connectorBounds(e, index) {
|
|
|
1368
1890
|
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
1369
1891
|
}
|
|
1370
1892
|
function midpoint(e, index) {
|
|
1371
|
-
const pts = routePoints(
|
|
1893
|
+
const pts = routePoints(route2(e, index));
|
|
1372
1894
|
if (pts.length <= 1) return pts[0] ?? { x: 0, y: 0 };
|
|
1373
1895
|
const lengths = [];
|
|
1374
1896
|
let total = 0;
|
|
@@ -1517,7 +2039,7 @@ function graphOf(frame2) {
|
|
|
1517
2039
|
graphs.set(els, { members: [...els], graph });
|
|
1518
2040
|
return graph;
|
|
1519
2041
|
}
|
|
1520
|
-
var
|
|
2042
|
+
var Frontier2 = class {
|
|
1521
2043
|
constructor() {
|
|
1522
2044
|
__publicField(this, "heap", []);
|
|
1523
2045
|
}
|
|
@@ -1571,7 +2093,7 @@ function grow({ shapes: shapes2, next }, from, offline, until) {
|
|
|
1571
2093
|
const dist = /* @__PURE__ */ new Map([[from, 0]]), order = /* @__PURE__ */ new Map([[from, 0]]);
|
|
1572
2094
|
let counter = 1;
|
|
1573
2095
|
const tree = new FlowTree(from);
|
|
1574
|
-
const open = new
|
|
2096
|
+
const open = new Frontier2();
|
|
1575
2097
|
open.push(from, 0, 0);
|
|
1576
2098
|
while (open.size > 0) {
|
|
1577
2099
|
const { id: n, order: o } = open.pop();
|
|
@@ -1677,6 +2199,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1677
2199
|
return pts;
|
|
1678
2200
|
};
|
|
1679
2201
|
const metrics2 = emptyMetrics();
|
|
2202
|
+
const routes = /* @__PURE__ */ new Map();
|
|
1680
2203
|
for (const e of frame2.elements) {
|
|
1681
2204
|
if (e.kind === "connector" && !e.connector) continue;
|
|
1682
2205
|
metrics2.elements.set(e.id, { throughput: 0, load: null, delay: e.kind !== "connector" ? delayOf(e) : 0, backlog: 0, inFlight: 0, depth: 0 });
|
|
@@ -1688,7 +2211,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1688
2211
|
if (c !== null && Number.isFinite(c)) room.set(e.id, Math.max(c, 0));
|
|
1689
2212
|
}
|
|
1690
2213
|
const full = () => new Set([...room].filter(([, v]) => v <= TINY).map(([id]) => id));
|
|
1691
|
-
const
|
|
2214
|
+
const route3 = (tree, goal) => {
|
|
1692
2215
|
const hops = [], shapes2 = [];
|
|
1693
2216
|
let at = goal;
|
|
1694
2217
|
for (let step = tree.came.get(at); step; step = tree.came.get(at)) {
|
|
@@ -1700,7 +2223,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1700
2223
|
return { hops: hops.reverse(), shapes: shapes2.reverse() };
|
|
1701
2224
|
};
|
|
1702
2225
|
const way = (hops, shapes2) => {
|
|
1703
|
-
const points = [],
|
|
2226
|
+
const points = [], inside2 = [], stretches = [];
|
|
1704
2227
|
let length = 0;
|
|
1705
2228
|
hops.forEach(([id, back], k) => {
|
|
1706
2229
|
const c = byID.get(id);
|
|
@@ -1711,7 +2234,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1711
2234
|
if (last) {
|
|
1712
2235
|
const gap = Math.hypot(line2[0].x - last.x, line2[0].y - last.y);
|
|
1713
2236
|
const shape = byID.get(shapes2[k]);
|
|
1714
|
-
|
|
2237
|
+
inside2.push([length, length + gap, shape ? delayOf(shape) : 0]);
|
|
1715
2238
|
length += gap;
|
|
1716
2239
|
}
|
|
1717
2240
|
const start2 = length;
|
|
@@ -1719,7 +2242,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1719
2242
|
stretches.push([id, start2, length]);
|
|
1720
2243
|
points.push(...line2);
|
|
1721
2244
|
});
|
|
1722
|
-
return length > 0 ? { line: new Polyline(points), length, inside, stretches } : null;
|
|
2245
|
+
return length > 0 ? { line: new Polyline(points), length, inside: inside2, stretches } : null;
|
|
1723
2246
|
};
|
|
1724
2247
|
const along = (w, elapsedAt, speed) => {
|
|
1725
2248
|
let elapsed = elapsedAt, d = 0;
|
|
@@ -1760,6 +2283,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1760
2283
|
if (flow.when !== null && values !== null && !holds(flow.when, values)) continue;
|
|
1761
2284
|
const source = byID.get(flow.from);
|
|
1762
2285
|
const base2 = source ? baseTree(flow.from) : null;
|
|
2286
|
+
routes.set(flow.id, []);
|
|
1763
2287
|
if (!source || !base2) continue;
|
|
1764
2288
|
const volume = amountIn(flow, values);
|
|
1765
2289
|
const fm = { volume, placed: 0, backlog: 0, backup: 0, transit: null };
|
|
@@ -1774,7 +2298,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1774
2298
|
const tree = flowTree(flow.from, frame2, union2(offline, full()), candidates[0]);
|
|
1775
2299
|
const goal2 = tree?.goals(candidates, false)[0];
|
|
1776
2300
|
if (!tree || goal2 === void 0) break;
|
|
1777
|
-
const r =
|
|
2301
|
+
const r = route3(tree, goal2);
|
|
1778
2302
|
const ids = [...r.shapes, ...r.hops.map(([id]) => id)];
|
|
1779
2303
|
const rooms = ids.filter((id) => room.has(id)).map((id) => room.get(id));
|
|
1780
2304
|
const fits = Math.min(left, rooms.length > 0 ? Math.min(...rooms) : Infinity);
|
|
@@ -1787,7 +2311,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1787
2311
|
let at = flow.from;
|
|
1788
2312
|
const goal = base2.goals(candidates, false)[0];
|
|
1789
2313
|
if (goal !== void 0) {
|
|
1790
|
-
const r =
|
|
2314
|
+
const r = route3(base2, goal), done = full();
|
|
1791
2315
|
const sequence = [[r.shapes[0], r.shapes[0]]];
|
|
1792
2316
|
r.hops.forEach(([id], k) => {
|
|
1793
2317
|
sequence.push([id, r.shapes[k]], [r.shapes[k + 1], r.shapes[k]]);
|
|
@@ -1816,6 +2340,13 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1816
2340
|
else waiting2.push([at, left]);
|
|
1817
2341
|
}
|
|
1818
2342
|
waitingBy.set(flow.id, waiting2);
|
|
2343
|
+
routes.set(flow.id, placements.map((p) => ({
|
|
2344
|
+
goal: p.goal,
|
|
2345
|
+
hops: p.hops,
|
|
2346
|
+
amount: p.amount,
|
|
2347
|
+
waits: p.waits === true,
|
|
2348
|
+
cost: p.hops.reduce((s, [id]) => s + routeCost(byID.get(id)?.connector?.cost ?? null), 0)
|
|
2349
|
+
})));
|
|
1819
2350
|
for (const p of placements) {
|
|
1820
2351
|
for (const id of [...p.shapes, ...p.hops.map(([h]) => h)]) bump(id, (m) => {
|
|
1821
2352
|
m.throughput += p.amount;
|
|
@@ -1898,7 +2429,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1898
2429
|
const tree = flowTree(at, frame2, union2(offline, blocked), flow.to[0]);
|
|
1899
2430
|
const goal = tree?.goals(flow.to, false)[0];
|
|
1900
2431
|
if (!tree || goal === void 0) break;
|
|
1901
|
-
const r =
|
|
2432
|
+
const r = route3(tree, goal);
|
|
1902
2433
|
const ids = [...r.shapes.slice(1), ...r.hops.map(([id]) => id)];
|
|
1903
2434
|
const rooms = ids.filter((id) => trial.has(id)).map((id) => trial.get(id));
|
|
1904
2435
|
const spare = rooms.length > 0 ? Math.min(...rooms) : Infinity;
|
|
@@ -1953,7 +2484,7 @@ function flowFrame(flows, frame2, t, values, offline = /* @__PURE__ */ new Set()
|
|
|
1953
2484
|
if (c === null || !Number.isFinite(c) || !m) continue;
|
|
1954
2485
|
m.load = c > 0 ? m.throughput / c : 1;
|
|
1955
2486
|
}
|
|
1956
|
-
return { packets, metrics: metrics2 };
|
|
2487
|
+
return { packets, metrics: metrics2, routes };
|
|
1957
2488
|
}
|
|
1958
2489
|
var BLANK = {
|
|
1959
2490
|
seed: 1,
|
|
@@ -2047,16 +2578,16 @@ function finishing(elements, metrics2, values, frame2, flows, since = Infinity,
|
|
|
2047
2578
|
}
|
|
2048
2579
|
var gaugeKey = (id) => "gauge|" + id;
|
|
2049
2580
|
var GAUGE_EASE = 1;
|
|
2050
|
-
var
|
|
2581
|
+
var hex2 = (h) => {
|
|
2051
2582
|
const v = parseInt(h.slice(1), 16);
|
|
2052
2583
|
return { r: (v >> 16 & 255) / 255, g: (v >> 8 & 255) / 255, b: (v & 255) / 255, a: 1 };
|
|
2053
2584
|
};
|
|
2054
|
-
var GAUGE_GREEN =
|
|
2055
|
-
var GAUGE_AMBER =
|
|
2056
|
-
var GAUGE_RED =
|
|
2057
|
-
var GAUGE_BLUE =
|
|
2058
|
-
var GAUGE_TRACK =
|
|
2059
|
-
var GAUGE_GRAY =
|
|
2585
|
+
var GAUGE_GREEN = hex2("#16a34a");
|
|
2586
|
+
var GAUGE_AMBER = hex2("#d97706");
|
|
2587
|
+
var GAUGE_RED = hex2("#dc2626");
|
|
2588
|
+
var GAUGE_BLUE = hex2("#2f6fdf");
|
|
2589
|
+
var GAUGE_TRACK = hex2("#e5e7eb");
|
|
2590
|
+
var GAUGE_GRAY = hex2("#9ca3af");
|
|
2060
2591
|
function formatted(metric, v) {
|
|
2061
2592
|
if (v === null || !Number.isFinite(v)) return "\u2013";
|
|
2062
2593
|
switch (metric) {
|
|
@@ -2342,17 +2873,17 @@ function clusterStrokes(c, angle2, size) {
|
|
|
2342
2873
|
});
|
|
2343
2874
|
}
|
|
2344
2875
|
function contains(contours, p) {
|
|
2345
|
-
let
|
|
2876
|
+
let inside2 = false;
|
|
2346
2877
|
for (const poly of contours) {
|
|
2347
2878
|
if (poly.length < 3) continue;
|
|
2348
2879
|
let k = poly.length - 1;
|
|
2349
2880
|
for (let i = 0; i < poly.length; i++) {
|
|
2350
2881
|
const a = poly[i], b = poly[k];
|
|
2351
|
-
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)
|
|
2882
|
+
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;
|
|
2352
2883
|
k = i;
|
|
2353
2884
|
}
|
|
2354
2885
|
}
|
|
2355
|
-
return
|
|
2886
|
+
return inside2;
|
|
2356
2887
|
}
|
|
2357
2888
|
function distance(p, contours) {
|
|
2358
2889
|
let best = Infinity;
|
|
@@ -2629,7 +3160,7 @@ function connectorParts(e, index, page) {
|
|
|
2629
3160
|
const c = e.connector;
|
|
2630
3161
|
if (!c) return [new Path(), new Path()];
|
|
2631
3162
|
const [a, b] = endpoints(e, index);
|
|
2632
|
-
const r =
|
|
3163
|
+
const r = route2(e, index);
|
|
2633
3164
|
const hops = c.hops === true && page ? hopCrossings(e, r, page, index) : [];
|
|
2634
3165
|
const key = [
|
|
2635
3166
|
e.seed,
|
|
@@ -2717,7 +3248,7 @@ function linesBeneath(e, page, index) {
|
|
|
2717
3248
|
}
|
|
2718
3249
|
const count = f.first.get(e.id) ?? f.under.length;
|
|
2719
3250
|
while (f.lines.length < count) {
|
|
2720
|
-
const points = routePoints(
|
|
3251
|
+
const points = routePoints(route2(f.under[f.lines.length], index));
|
|
2721
3252
|
f.lines.push({ points, box: boxOf(points) });
|
|
2722
3253
|
}
|
|
2723
3254
|
return { lines: f.lines, count };
|
|
@@ -2731,7 +3262,7 @@ function hopCrossings(e, r, page, index) {
|
|
|
2731
3262
|
for (const samples of segments) {
|
|
2732
3263
|
const along = runningLengths(samples);
|
|
2733
3264
|
const length = along[along.length - 1];
|
|
2734
|
-
const
|
|
3265
|
+
const found2 = [];
|
|
2735
3266
|
const near = [];
|
|
2736
3267
|
const whole = boxOf(samples);
|
|
2737
3268
|
for (let n = 0; n < count; n++) if (touching(whole, lines[n].box)) near.push(lines[n]);
|
|
@@ -2748,12 +3279,12 @@ function hopCrossings(e, r, page, index) {
|
|
|
2748
3279
|
if (Math.abs(det) < 1e-9) continue;
|
|
2749
3280
|
const wx = a.x - p.x, wy = a.y - p.y;
|
|
2750
3281
|
const t = (wx * ey - wy * ex) / det, s = (wx * dy - wy * dx) / det;
|
|
2751
|
-
if (t > 0 && t < 1 && s >= 0 && s <= 1)
|
|
3282
|
+
if (t > 0 && t < 1 && s >= 0 && s <= 1) found2.push(along[i] + t * (along[i + 1] - along[i]));
|
|
2752
3283
|
}
|
|
2753
3284
|
}
|
|
2754
3285
|
}
|
|
2755
3286
|
const kept = [];
|
|
2756
|
-
for (const at of
|
|
3287
|
+
for (const at of found2.sort((x, y) => x - y)) {
|
|
2757
3288
|
if (at < rad + 2 || at > length - rad - 2) continue;
|
|
2758
3289
|
if (kept.length > 0 && at - kept[kept.length - 1] < 2 * rad + 2) continue;
|
|
2759
3290
|
kept.push(at);
|
|
@@ -2967,7 +3498,7 @@ var lerpColor = (a, b, u) => ({
|
|
|
2967
3498
|
b: mix01(a.b, b.b, u),
|
|
2968
3499
|
a: mix01(a.a, b.a, u)
|
|
2969
3500
|
});
|
|
2970
|
-
function
|
|
3501
|
+
function num3(track, t, p, base2) {
|
|
2971
3502
|
return value(track, t, (k) => k[p], base2, lerpNum);
|
|
2972
3503
|
}
|
|
2973
3504
|
function isChanging(track, t) {
|
|
@@ -2993,20 +3524,20 @@ function isVisible(track, t) {
|
|
|
2993
3524
|
function applyTrack(track, e, t) {
|
|
2994
3525
|
e = cloneElement(e);
|
|
2995
3526
|
if (track.keys.some((k) => k.reveal !== void 0)) {
|
|
2996
|
-
const r =
|
|
3527
|
+
const r = num3(track, t, "reveal", e.reveal ?? 1);
|
|
2997
3528
|
e.reveal = r >= 1 ? null : Math.max(0, r);
|
|
2998
3529
|
}
|
|
2999
3530
|
if (track.keys.some((k) => k.opacity !== void 0)) {
|
|
3000
|
-
const o = Math.min(Math.max(
|
|
3531
|
+
const o = Math.min(Math.max(num3(track, t, "opacity", e.style.opacity ?? 1), 0), 1);
|
|
3001
3532
|
e.style.opacity = o >= 1 ? null : o;
|
|
3002
3533
|
}
|
|
3003
3534
|
if (e.kind === "connector") return e;
|
|
3004
|
-
e.x =
|
|
3005
|
-
e.y =
|
|
3006
|
-
e.width = Math.max(0,
|
|
3007
|
-
e.height = Math.max(0,
|
|
3008
|
-
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation =
|
|
3009
|
-
e.style.strokeWidth = Math.max(0,
|
|
3535
|
+
e.x = num3(track, t, "x", e.x);
|
|
3536
|
+
e.y = num3(track, t, "y", e.y);
|
|
3537
|
+
e.width = Math.max(0, num3(track, t, "width", e.width));
|
|
3538
|
+
e.height = Math.max(0, num3(track, t, "height", e.height));
|
|
3539
|
+
if (track.keys.some((k) => k.rotation !== void 0)) e.rotation = num3(track, t, "rotation", angle(e));
|
|
3540
|
+
e.style.strokeWidth = Math.max(0, num3(track, t, "strokeWidth", e.style.strokeWidth));
|
|
3010
3541
|
e.style.stroke = value(track, t, (k) => k.stroke, e.style.stroke, lerpColor);
|
|
3011
3542
|
e.style.fill = value(track, t, (k) => k.fill, e.style.fill, lerpColor);
|
|
3012
3543
|
return e;
|
|
@@ -3021,7 +3552,7 @@ function followSpan(track, t) {
|
|
|
3021
3552
|
return f;
|
|
3022
3553
|
}
|
|
3023
3554
|
function tracing(e, page, index = indexOf(page)) {
|
|
3024
|
-
if (e.kind === "connector") return new Polyline(routePoints(
|
|
3555
|
+
if (e.kind === "connector") return new Polyline(routePoints(route2(e, index)));
|
|
3025
3556
|
const clean = cloneElement(e);
|
|
3026
3557
|
clean.style.roughness = 0;
|
|
3027
3558
|
const pts = outline(clean).contours()[0] ?? [];
|
|
@@ -3082,7 +3613,8 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3082
3613
|
}
|
|
3083
3614
|
const flowing = (page.flows ?? []).length > 0;
|
|
3084
3615
|
const measuring = flowing || page.elements.some((e) => e.gauge || (e.overrides ?? []).some((o) => mentionsMetric(o.when)));
|
|
3085
|
-
const
|
|
3616
|
+
const fills = values !== null && Object.keys(values).length > 0 && page.elements.some((e) => e.text?.includes("{") || (e.overrides ?? []).some((o) => o.text?.includes("{")));
|
|
3617
|
+
const timeline = page.timeline ?? (off.size === 0 && active.size === 0 && !measuring && !fills ? null : EMPTY_TIMELINE);
|
|
3086
3618
|
if (!timeline) return { frame: page, metrics: emptyMetrics() };
|
|
3087
3619
|
t = clampTime(t);
|
|
3088
3620
|
const tracks = trackMap(timeline);
|
|
@@ -3118,6 +3650,10 @@ function posedWithMetrics(page, t, state = "initial", since = null, carried = {}
|
|
|
3118
3650
|
const track = tracks.get(e.id);
|
|
3119
3651
|
let out = track ? applyTrack(track, e, t) : e;
|
|
3120
3652
|
for (const o of active.get(e.id) ?? []) out = applyOverride(o, out);
|
|
3653
|
+
if (fills && values && out.text) {
|
|
3654
|
+
const text = filling(out.text, values);
|
|
3655
|
+
if (text !== out.text) out = { ...out, text };
|
|
3656
|
+
}
|
|
3121
3657
|
const list = active.get(e.id);
|
|
3122
3658
|
if (list) {
|
|
3123
3659
|
const caps = list.map((o) => o.capacity).filter((c) => c !== null && Number.isFinite(c));
|
|
@@ -3301,10 +3837,10 @@ var Expansions = class {
|
|
|
3301
3837
|
this.posing = posing;
|
|
3302
3838
|
}
|
|
3303
3839
|
};
|
|
3304
|
-
function expandingSymbols(page, t, posing = true, times = /* @__PURE__ */ new Map(),
|
|
3840
|
+
function expandingSymbols(page, t, posing = true, times = /* @__PURE__ */ new Map(), memo2) {
|
|
3305
3841
|
if (!hasInstances(page)) return page;
|
|
3306
3842
|
const budget = { left: MAX_ELEMENTS };
|
|
3307
|
-
const m =
|
|
3843
|
+
const m = memo2 && memo2.page === page && memo2.t === t && memo2.posing === posing ? memo2 : void 0;
|
|
3308
3844
|
return { ...page, elements: expand(page.elements, page, t, symbolTable(page), [], posing, budget, times, m) };
|
|
3309
3845
|
}
|
|
3310
3846
|
function symbolTable(page) {
|
|
@@ -3312,7 +3848,7 @@ function symbolTable(page) {
|
|
|
3312
3848
|
for (const s of page.symbols ?? []) if (!table.has(s.id)) table.set(s.id, s);
|
|
3313
3849
|
return table;
|
|
3314
3850
|
}
|
|
3315
|
-
function expand(elements, page, t, table, stack, posing, budget, times = /* @__PURE__ */ new Map(),
|
|
3851
|
+
function expand(elements, page, t, table, stack, posing, budget, times = /* @__PURE__ */ new Map(), memo2) {
|
|
3316
3852
|
const instances = new Set(elements.filter((e) => e.kind === "symbol").map((e) => e.id));
|
|
3317
3853
|
if (instances.size === 0) {
|
|
3318
3854
|
budget.left -= elements.length;
|
|
@@ -3338,7 +3874,7 @@ function expand(elements, page, t, table, stack, posing, budget, times = /* @__P
|
|
|
3338
3874
|
continue;
|
|
3339
3875
|
}
|
|
3340
3876
|
const override0 = stack.length === 0 ? times.get(e.id) : void 0;
|
|
3341
|
-
const was =
|
|
3877
|
+
const was = memo2?.made.get(e);
|
|
3342
3878
|
if (was && was.override === override0 && was.after > 0 && budget.left - (was.before - was.after) > 0) {
|
|
3343
3879
|
budget.left -= was.before - was.after;
|
|
3344
3880
|
for (const x of was.out) out.push(x);
|
|
@@ -3360,7 +3896,7 @@ function expand(elements, page, t, table, stack, posing, budget, times = /* @__P
|
|
|
3360
3896
|
layers: [{ id: e.layerID, name: "", visible: true, locked: false }]
|
|
3361
3897
|
};
|
|
3362
3898
|
for (const x of expand(drawn.elements, drawn, u, table, [...stack, id], posing, budget)) out.push(placed(x, e, symbol));
|
|
3363
|
-
if (
|
|
3899
|
+
if (memo2 && stack.length === 0 && !was) memo2.made.set(e, { override: override0, before, after: budget.left, out: out.slice(from) });
|
|
3364
3900
|
}
|
|
3365
3901
|
return out;
|
|
3366
3902
|
}
|
|
@@ -3413,8 +3949,8 @@ function mix2(x) {
|
|
|
3413
3949
|
return z ^ z >> 31n;
|
|
3414
3950
|
}
|
|
3415
3951
|
function halves(id) {
|
|
3416
|
-
const
|
|
3417
|
-
return [BigInt(`0x${
|
|
3952
|
+
const hex3 = id.replace(/-/g, "").padStart(32, "0").slice(-32);
|
|
3953
|
+
return [BigInt(`0x${hex3.slice(0, 16)}`), BigInt(`0x${hex3.slice(16)}`)];
|
|
3418
3954
|
}
|
|
3419
3955
|
function derivedID(instance, inner) {
|
|
3420
3956
|
const [a, b] = halves(instance), [c, d] = halves(inner);
|
|
@@ -3435,7 +3971,12 @@ function clickTargets(page, file) {
|
|
|
3435
3971
|
let pageOf = null;
|
|
3436
3972
|
for (const e of page.elements) {
|
|
3437
3973
|
const c = e.click;
|
|
3438
|
-
const state = c && changesState(c) ? {
|
|
3974
|
+
const state = c && changesState(c) ? {
|
|
3975
|
+
...c.set ? { set: c.set } : {},
|
|
3976
|
+
...c.toggle ? { toggle: c.toggle } : {},
|
|
3977
|
+
...c.reset ? { reset: true } : {},
|
|
3978
|
+
...c.run ? { run: c.run } : {}
|
|
3979
|
+
} : null;
|
|
3439
3980
|
if (c?.scene) {
|
|
3440
3981
|
const i = sceneNamed(c.scene, markers);
|
|
3441
3982
|
if (i >= 0) {
|
|
@@ -3742,12 +4283,23 @@ function inline(src, style2 = {}) {
|
|
|
3742
4283
|
i += m[0].length;
|
|
3743
4284
|
continue;
|
|
3744
4285
|
}
|
|
3745
|
-
if (m = /^(
|
|
4286
|
+
if (m = /^(\*\*\*|___)(?=\S)([\s\S]*?\S)\1(?!\*|_)/.exec(rest)) {
|
|
3746
4287
|
emit();
|
|
3747
|
-
out.push(...inline(m[2], { ...style2, bold: true }));
|
|
4288
|
+
out.push(...inline(m[2], { ...style2, bold: true, italic: true }));
|
|
3748
4289
|
i += m[0].length;
|
|
3749
4290
|
continue;
|
|
3750
4291
|
}
|
|
4292
|
+
if (m = /^(\*\*|__)(?=\S)([\s\S]*?\S)\1/.exec(rest)) {
|
|
4293
|
+
let body = m[2], length = m[0].length;
|
|
4294
|
+
if (m[1] === "**" && rest[length] === "*" && (body.replace(/\*\*/g, "").match(/\*/g) ?? []).length % 2 === 1) {
|
|
4295
|
+
body += "*";
|
|
4296
|
+
length++;
|
|
4297
|
+
}
|
|
4298
|
+
emit();
|
|
4299
|
+
out.push(...inline(body, { ...style2, bold: true }));
|
|
4300
|
+
i += length;
|
|
4301
|
+
continue;
|
|
4302
|
+
}
|
|
3751
4303
|
if ((m = /^(\*|_)(?=\S)([\s\S]*?\S)\1(?!\1)/.exec(rest)) && (m[1] === "*" || !/\w/.test(src[i - 1] ?? ""))) {
|
|
3752
4304
|
emit();
|
|
3753
4305
|
out.push(...inline(m[2], { ...style2, italic: true }));
|
|
@@ -4131,20 +4683,6 @@ function sameStyle(a, b) {
|
|
|
4131
4683
|
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;
|
|
4132
4684
|
}
|
|
4133
4685
|
|
|
4134
|
-
// src/theme.ts
|
|
4135
|
-
var rgb = (r, g, b) => ({ r, g, b, a: 1 });
|
|
4136
|
-
var themes = [
|
|
4137
|
-
{ id: "paper", name: "Paper", background: rgb(0.984, 0.98, 0.965), ink: rgb(0.12, 0.12, 0.13) },
|
|
4138
|
-
{ id: "white", name: "White", background: rgb(1, 1, 1), ink: rgb(0.12, 0.12, 0.13) },
|
|
4139
|
-
{ id: "sepia", name: "Sepia", background: rgb(0.953, 0.914, 0.824), ink: rgb(0.23, 0.18, 0.13) },
|
|
4140
|
-
{ id: "blueprint", name: "Blueprint", background: rgb(0.106, 0.227, 0.361), ink: rgb(0.91, 0.945, 0.984) },
|
|
4141
|
-
{ id: "chalkboard", name: "Chalkboard", background: rgb(0.169, 0.227, 0.2), ink: rgb(0.945, 0.945, 0.918) },
|
|
4142
|
-
{ id: "midnight", name: "Midnight", background: rgb(0.082, 0.086, 0.102), ink: rgb(0.925, 0.925, 0.94) }
|
|
4143
|
-
];
|
|
4144
|
-
function themeNamed(id) {
|
|
4145
|
-
return themes.find((t) => t.id === id) ?? null;
|
|
4146
|
-
}
|
|
4147
|
-
|
|
4148
4686
|
// src/svg.ts
|
|
4149
4687
|
function bounds(page, padding = 24) {
|
|
4150
4688
|
if (hasInstances(page)) return bounds(expandingSymbols(page, 0), padding);
|
|
@@ -4210,7 +4748,7 @@ function svgOpen(b, background, paint) {
|
|
|
4210
4748
|
}
|
|
4211
4749
|
function svgParts(page, background, fixed, links = false, only) {
|
|
4212
4750
|
if (hasInstances(page)) return svgParts(expandingSymbols(page, 0), background, fixed, links, only);
|
|
4213
|
-
const theme =
|
|
4751
|
+
const theme = pageTheme(page);
|
|
4214
4752
|
const bg = background ? theme?.background ?? background : null;
|
|
4215
4753
|
const ink = theme?.ink ?? black;
|
|
4216
4754
|
const d = { ...page, elements: page.elements.map((e) => resolvingInk(e, ink)) };
|
|
@@ -4246,7 +4784,7 @@ ${body}</a>
|
|
|
4246
4784
|
}
|
|
4247
4785
|
function svg(page, background = { r: 1, g: 1, b: 1, a: 1 }, fixed) {
|
|
4248
4786
|
const { bounds: b, parts } = svgParts(page, background, fixed, true);
|
|
4249
|
-
return svgOpen(b,
|
|
4787
|
+
return svgOpen(b, pageTheme(page)?.background ?? background, background !== null) + parts.map((p) => p.markup).join("") + "</svg>\n";
|
|
4250
4788
|
}
|
|
4251
4789
|
function labelRect(e, index) {
|
|
4252
4790
|
const m = midpoint(e, index);
|
|
@@ -4340,7 +4878,7 @@ function svgElement(e, index, labelBackground, page) {
|
|
|
4340
4878
|
}
|
|
4341
4879
|
s += `<path d="${outline(e).data()}" fill="none" ${strokeAttrs}/>
|
|
4342
4880
|
`;
|
|
4343
|
-
if (text) s += e.kind === "note" ? noteMarkup(text, e) : textElement(text, e.kind === "path" ? pathLabelBox(e) : frame(e), e, true);
|
|
4881
|
+
if (text) s += e.kind === "note" ? noteMarkup(text, e) : textElement(text, e.kind === "path" || e.kind === "rectangle" ? pathLabelBox(e) : frame(e), e, true);
|
|
4344
4882
|
return s;
|
|
4345
4883
|
}
|
|
4346
4884
|
default:
|
|
@@ -4461,6 +4999,40 @@ function start() {
|
|
|
4461
4999
|
return w;
|
|
4462
5000
|
}
|
|
4463
5001
|
|
|
5002
|
+
// src/logic-loader.ts
|
|
5003
|
+
var scriptSrc = typeof document !== "undefined" ? document.currentScript?.src ?? null : null;
|
|
5004
|
+
var found = null;
|
|
5005
|
+
var present = () => globalThis.DottieLogic;
|
|
5006
|
+
function logicEngine() {
|
|
5007
|
+
found ?? (found = (async () => {
|
|
5008
|
+
if (!present()) {
|
|
5009
|
+
const here = import.meta.url;
|
|
5010
|
+
const base2 = here ?? scriptSrc;
|
|
5011
|
+
if (!base2) throw new Error("No dottie-logic.min.js on the page, and no address to find it from");
|
|
5012
|
+
const url = new URL("./dottie-logic.min.js", base2).href;
|
|
5013
|
+
if (here) await import(
|
|
5014
|
+
/* @vite-ignore */
|
|
5015
|
+
url
|
|
5016
|
+
);
|
|
5017
|
+
else await new Promise((resolve2, reject) => {
|
|
5018
|
+
const s = document.createElement("script");
|
|
5019
|
+
s.src = url;
|
|
5020
|
+
s.onload = () => resolve2();
|
|
5021
|
+
s.onerror = () => reject(new Error(`Couldn't load ${url}`));
|
|
5022
|
+
document.head.append(s);
|
|
5023
|
+
});
|
|
5024
|
+
}
|
|
5025
|
+
const engine = present();
|
|
5026
|
+
if (!engine) throw new Error("dottie-logic.min.js didn't load");
|
|
5027
|
+
await engine.loadLogic();
|
|
5028
|
+
return engine;
|
|
5029
|
+
})());
|
|
5030
|
+
found.catch(() => {
|
|
5031
|
+
found = null;
|
|
5032
|
+
});
|
|
5033
|
+
return found;
|
|
5034
|
+
}
|
|
5035
|
+
|
|
4464
5036
|
// src/player.ts
|
|
4465
5037
|
var WHITE = { r: 1, g: 1, b: 1, a: 1 };
|
|
4466
5038
|
function pageIndex(file, spec) {
|
|
@@ -4568,7 +5140,7 @@ var Playback = class {
|
|
|
4568
5140
|
this.sceneNames = (page.timeline?.markers ?? []).map((m) => m.name);
|
|
4569
5141
|
this.sceneLoops = (page.timeline?.markers ?? []).map((m) => m.loop === true);
|
|
4570
5142
|
this.state = { ...page.variables ?? {} };
|
|
4571
|
-
const theme =
|
|
5143
|
+
const theme = pageTheme(page);
|
|
4572
5144
|
this.background = theme?.background ?? WHITE;
|
|
4573
5145
|
this.ink = theme?.ink ?? { r: 0.12, g: 0.12, b: 0.13, a: 1 };
|
|
4574
5146
|
const ns = "http://www.w3.org/2000/svg";
|
|
@@ -4802,6 +5374,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
4802
5374
|
__publicField(this, "stillTime", null);
|
|
4803
5375
|
__publicField(this, "visible", true);
|
|
4804
5376
|
__publicField(this, "observer", null);
|
|
5377
|
+
/** Runs handler `handler` from the page's logic for a click on `id` (DottieDocument.clicked). Clicks take turns, so
|
|
5378
|
+
* each handler sees the state the one before left. */
|
|
5379
|
+
__publicField(this, "logicQueue", Promise.resolve());
|
|
4805
5380
|
this.root = this.attachShadow({ mode: "open" });
|
|
4806
5381
|
this.root.innerHTML = `<style>${STYLE}</style><div class="stage" part="stage" role="img"></div>
|
|
4807
5382
|
<div class="bar" part="controls" hidden>
|
|
@@ -5028,7 +5603,9 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5028
5603
|
this.scheduleReplay();
|
|
5029
5604
|
}
|
|
5030
5605
|
if (!click) return;
|
|
5031
|
-
if (click.
|
|
5606
|
+
if (click.run && p.page.logic && this.hasAttribute("allow-logic")) {
|
|
5607
|
+
this.runLogic(click.run, id, click.reset === true);
|
|
5608
|
+
} else if (click.set || click.toggle || click.reset) {
|
|
5032
5609
|
this.setState(applyClick(
|
|
5033
5610
|
{ scene: null, url: null, set: click.set ?? null, toggle: click.toggle ?? null, reset: click.reset ?? null },
|
|
5034
5611
|
p.state,
|
|
@@ -5050,6 +5627,35 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5050
5627
|
}
|
|
5051
5628
|
}
|
|
5052
5629
|
}
|
|
5630
|
+
runLogic(handler, id, reset) {
|
|
5631
|
+
const p = this.playback;
|
|
5632
|
+
if (!p) return;
|
|
5633
|
+
const e = p.page.elements.find((x) => x.id === id);
|
|
5634
|
+
const s = p.sceneAt(p.frame);
|
|
5635
|
+
const event = {
|
|
5636
|
+
id,
|
|
5637
|
+
anchor: e?.anchor ?? null,
|
|
5638
|
+
text: e?.text?.split("\n")[0] ?? null,
|
|
5639
|
+
time: this.currentTime,
|
|
5640
|
+
scene: s < 0 ? null : p.sceneNames[s]
|
|
5641
|
+
};
|
|
5642
|
+
this.logicQueue = this.logicQueue.then(async () => {
|
|
5643
|
+
const engine = await logicEngine();
|
|
5644
|
+
const now = this.playback;
|
|
5645
|
+
if (!now || now !== p || !now.page.logic) return;
|
|
5646
|
+
let start2 = now.state;
|
|
5647
|
+
if (reset) {
|
|
5648
|
+
start2 = {};
|
|
5649
|
+
for (const k of Object.keys(now.state)) start2[k] = false;
|
|
5650
|
+
Object.assign(start2, now.page.variables ?? {});
|
|
5651
|
+
}
|
|
5652
|
+
const r = engine.runLogic(now.page.logic, handler, start2, event);
|
|
5653
|
+
if (r.ok) this.setState({ ...start2, ...r.set });
|
|
5654
|
+
else this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: r.error } }));
|
|
5655
|
+
}).catch((err) => {
|
|
5656
|
+
this.dispatchEvent(new CustomEvent("logicerror", { detail: { element: id, handler, error: err.message } }));
|
|
5657
|
+
});
|
|
5658
|
+
}
|
|
5053
5659
|
/** The page's variables now. */
|
|
5054
5660
|
get variables() {
|
|
5055
5661
|
return { ...this.playback?.state ?? {} };
|
|
@@ -5156,7 +5762,7 @@ var DottiePlayer = class extends ElementBase {
|
|
|
5156
5762
|
if (handled) e.preventDefault();
|
|
5157
5763
|
}
|
|
5158
5764
|
};
|
|
5159
|
-
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label"]);
|
|
5765
|
+
__publicField(DottiePlayer, "observedAttributes", ["src", "page", "at", "clean", "no-controls", "label", "allow-logic"]);
|
|
5160
5766
|
function clickable(target) {
|
|
5161
5767
|
const el = target;
|
|
5162
5768
|
return el && typeof el.closest === "function" ? el.closest(".dottie-a") : null;
|
|
@@ -5222,7 +5828,7 @@ function inspectFrame(page, t) {
|
|
|
5222
5828
|
const [a, b] = endpoints(e, index);
|
|
5223
5829
|
d.start = pt(a);
|
|
5224
5830
|
d.end = pt(b);
|
|
5225
|
-
d.route = routePoints(
|
|
5831
|
+
d.route = routePoints(route2(e, index)).map(pt);
|
|
5226
5832
|
}
|
|
5227
5833
|
return d;
|
|
5228
5834
|
});
|
|
@@ -5261,7 +5867,7 @@ function render(source, options = {}) {
|
|
|
5261
5867
|
function isFile(v) {
|
|
5262
5868
|
return typeof v === "object" && v !== null && Array.isArray(v.pages) && Array.isArray(v.warnings);
|
|
5263
5869
|
}
|
|
5264
|
-
var VERSION = "0.
|
|
5870
|
+
var VERSION = "0.11.0";
|
|
5265
5871
|
export {
|
|
5266
5872
|
BOIL_DRAWINGS,
|
|
5267
5873
|
BUNDLED_FONTS,
|
|
@@ -5297,6 +5903,8 @@ export {
|
|
|
5297
5903
|
luminance,
|
|
5298
5904
|
orderedElements,
|
|
5299
5905
|
pageIndex,
|
|
5906
|
+
pageTheme,
|
|
5907
|
+
paletteColor,
|
|
5300
5908
|
parseDottie,
|
|
5301
5909
|
plan,
|
|
5302
5910
|
posterTime,
|